使用最新版springCloud版本教你一步一步搭建起springcloud alibaba开发环境。
1 springBoot、springCloud以及springCloud Alibaba版本
下图为springCloud Alibaba推荐的springBoot、springCloud适配版本,也是最新的版本:
2 搭建springboot工程
首先搭建一个spirngBoot工程,后面再一步一步引入cloud alibaba组件。
2.1 搭建cloudAlibaba父工程
父工程其实就一个空的maven工程,主要是引入相关依赖,并对依赖版本号进行约束。pom文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<packaging>pom</packaging>
<groupId>com.tdt.platform.cloud</groupId>
<artifactId>platform-cloud-b-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<modules>
<module>cloud-common</module>
<module>cloud-user-service</module>
</modules>
<properties>
<java.version>17</java.version>
<spring.cloud.version>2021.0.4</spring.cloud.version>
<spring.cloud.alibaba.version>2021.0.4.0</spring.cloud.alibaba.version>
<spring.boot.version>2.6.11</spring.boot.version>
<mybatis.plus.version>3.5.2</mybatis.plus.version>
<mysql.connector.java.version>8.0.30</mysql.connector.java.version>
<druid.spring.boot.starter.version>1.2.13</druid.spring.boot.starter.version>
<mapstruct.version>1.5.3.Final</mapstruct.version>
<lombok.version>1.18.24</lombok.version>
<lombok.mapstruct.binding.version>0.2.0</lombok.mapstruct.binding.version>
<maven.compiler.plugin.version>3.10.1</maven.compiler.plugin.version>
</properties>
<dependencyManagement>
<dependencies>
<!-- 整合spring-cloud-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- 整合spring-cloud-alibaba-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring.cloud.alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- springboot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- mybatis plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis.plus.version}</version>
</dependency>
<!-- mysql connector -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.connector.java.version}</version>
</dependency>
<!-- druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.spring.boot.starter.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.15</version>
</dependency>
<!-- mapstruct -->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${mapstruct.version}</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${mapstruct.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven.compiler.plugin.version}</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>UTF-8</encoding>
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${mapstruct.version}</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>${lombok.mapstruct.binding.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring.boot.version}</version>
</plugin>
</plugins>
</build>
</project>
2.2 搭建common子工程
common工程主要是做一些公共操作。目前只是封装了接口相应体对象,以及异常的基础处理。后面随着组件的引入会封装成独立的springboot starter或者cloud starter。
1. pom文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
<parent>
<groupId>com.tdt.platform.cloud</groupId>
<artifactId>platform-cloud-b-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>cloud-common</artifactId>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
</project>
2. 公共响应对象JsonResult
public class JsonResult<T> implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 请求成功
*/
private static final boolean REQUEST_SUCCESS = true;
/**
* 请求失败
*/
private static final boolean REQUEST_FAIL = false;
/**
* 默认错误码
*/
private static final String DEFAULT_ERROR_CODE = "-1";
/**
* 请求是否成功
*/
private Boolean success;
/**
* 业务数据
*/
private T data;
/**
* 错误码
*/
private String errorCode;
/**
* 错误提示语
*/
private String errorMessage;
public JsonResult() {
}
public JsonResult(Boolean success, T data, String errorCode, String errorMessage) {
this.success = success;
this.data = data;
this.errorCode = errorCode;
this.errorMessage = errorMessage;
}
/**
* 成功,不用返回数据
*
* @return
*/
public static <T> JsonResult<T> buildSuccess() {
return new JsonResult<>(REQUEST_SUCCESS, null, null, null);
}
/**
* 成功,返回数据
*
* @param data
* @return
*/
public static <T> JsonResult<T> buildSuccess(T data) {
return new JsonResult<>(REQUEST_SUCCESS, data, null, null);
}
/**
* 失败,固定状态码
*
* @param errorMsg
* @return
*/
public static <T> JsonResult<T> buildError(String errorMsg) {
return new JsonResult<>(REQUEST_FAIL, null, DEFAULT_ERROR_CODE, errorMsg);
}
/**
* 失败,自定义错误码和信息
*
* @param errorCode 错误码
* @param errorMsg 错误提示
* @return
*/
public static <T> JsonResult<T> buildError(String errorCode, String errorMsg) {
return new JsonResult<>(REQUEST_FAIL, null, errorCode, errorMsg);
}
/**
* 失败,枚举类定义错误码和信息
*
* @param baseErrorCodeEnum
* @return
*/
public static <T> JsonResult<T> buildError(BaseErrorCodeEnum baseErrorCodeEnum) {
return new JsonResult<>(REQUEST_FAIL, null, baseErrorCodeEnum.getErrorCode(), baseErrorCodeEnum.getErrorMsg());
}
// 省略get/set方法
}
3. 公共异常
定义一个异常错误码枚举的抽象接口:
public interface BaseErrorCodeEnum {
String getErrorCode();
String getErrorMsg();
}
系统通用的异常错误编号和描述枚举类:
public enum CommonErrorCodeEnum implements BaseErrorCodeEnum {
/**
* 系统未知错误
*/
SYSTEM_UNKNOWN_ERROR("-1", "系统未知错误"),
/**
* 客户端HTTP请求方法错误
* org.springframework.web.HttpRequestMethodNotSupportedException
*/
CLIENT_HTTP_METHOD_ERROR("1001", "客户端HTTP请求方法错误"),
/**
* 客户端request body参数错误
* 主要是未能通过Hibernate Validator校验的异常处理
* org.springframework.web.bind.MethodArgumentNotValidException
*/
CLIENT_REQUEST_BODY_CHECK_ERROR("1002", "客户端请求体参数校验不通过"),
/**
* 客户端@RequestBody请求体JSON格式错误或字段类型错误
* org.springframework.http.converter.HttpMessageNotReadableException
* <p>
* eg:
* 1、参数类型不对:{"test":"abc"},本身类型是Long
* 2、{"test":} test属性没有给值
*/
CLIENT_REQUEST_BODY_FORMAT_ERROR("1003", "客户端请求体JSON格式错误或字段类型不匹配"),
/**
* 客户端@PathVariable参数错误
* 一般是类型不匹配,比如本来是Long类型,客户端却给了一个无法转换成Long字符串
* org.springframework.validation.BindException
*/
CLIENT_PATH_VARIABLE_ERROR("1004", "客户端URL中的参数类型错误"),
/**
* 客户端@RequestParam参数校验不通过
* 主要是未能通过Hibernate Validator校验的异常处理
* javax.validation.ConstraintViolationException
*/
CLIENT_REQUEST_PARAM_CHECK_ERROR("1005", "客户端请求参数校验不通过"),
/**
* 客户端@RequestParam参数必填
* 入参中的@RequestParam注解设置了必填,但是客户端没有给值
* javax.validation.ConstraintViolationException
*/
CLIENT_REQUEST_PARAM_REQUIRED_ERROR("1006", "客户端请求缺少必填的参数"),
/**
* 通用的业务方法入参检查错误
* java.lang.IllegalArgumentException
*/
SERVER_ILLEGAL_ARGUMENT_ERROR("2001", "业务方法参数检查不通过"),
;
private String errorCode;
private String errorMsg;
CommonErrorCodeEnum(String errorCode, String errorMsg) {
this.errorCode = errorCode;
this.errorMsg = errorMsg;
}
@Override
public String getErrorCode() {
return errorCode;
}
public void setErrorCode(String errorCode) {
this.errorCode = errorCode;
}
@Override
public String getErrorMsg() {
return errorMsg;
}
public void setErrorMsg(String errorMsg) {
this.errorMsg = errorMsg;
}
}
4. 公共自定义异常
public class BaseBizException extends RuntimeException {
/**
* 默认错误码
*/
private static final String DEFAULT_ERROR_CODE = "-1";
private String errorCode;
private String errorMsg;
public BaseBizException(String errorMsg) {
super(errorMsg);
this.errorCode = DEFAULT_ERROR_CODE;
this.errorMsg = errorMsg;
}
public BaseBizException(String errorCode, String errorMsg) {
super(errorMsg);
this.errorCode = errorCode;
this.errorMsg = errorMsg;
}
public BaseBizException(BaseErrorCodeEnum baseErrorCodeEnum) {
super(baseErrorCodeEnum.getErrorMsg());
this.errorCode = baseErrorCodeEnum.getErrorCode();
this.errorMsg = baseErrorCodeEnum.getErrorMsg();
}
public BaseBizException(String errorCode, String errorMsg, Object... arguments) {
super(MessageFormat.format(errorMsg, arguments));
this.errorCode = errorCode;
this.errorMsg = MessageFormat.format(errorMsg, arguments);
}
public BaseBizException(BaseErrorCodeEnum baseErrorCodeEnum, Object... arguments) {
super(MessageFormat.format(baseErrorCodeEnum.getErrorMsg(), arguments));
this.errorCode = baseErrorCodeEnum.getErrorCode();
this.errorMsg = MessageFormat.format(baseErrorCodeEnum.getErrorMsg(), arguments);
}
// 省略get/set
}
2.3 搭建一个springboot工程
示例sql:
CREATE TABLE `user` (
`id` bigint NOT NULL,
`username` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
`age` int DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
pom文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
<parent>
<groupId>com.tdt.platform.cloud</groupId>
<artifactId>platform-cloud-b-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>cloud-user-service</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!-- mybatis mysql -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
</dependency>
<!-- common -->
<dependency>
<groupId>com.tdt.platform.cloud</groupId>
<artifactId>cloud-common</artifactId>
<version>${version}</version>
</dependency>
<!-- bean copy-->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
</project>
application.yml配置文件:
# 配置端口
server:
port: 9001
servlet:
context-path: /cloud-user-service
spring:
application:
name: cloud-user-service
datasource:
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://127.0.0.1:3306/cloud-user?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
username: root
password: 12345678
driverClassName: com.mysql.cj.jdbc.Driver
initialSize: 50
minIdle: 50
maxActive: 500
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
maxPoolPreparedStatementPerConnectionSize: 20
filters: stat,wall,log4j
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
map-underscore-to-camel-case: true
mapper-locations: classpath:mapper/*.xml
全局异常处理:
@Slf4j
@RestControllerAdvice
@Order
public class GlobalExceptionHandler {
/**
* 1001 HTTP请求方法类型错误
*
* @param e
* @return
*/
@ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)
public JsonResult<Object> handle(HttpRequestMethodNotSupportedException e) {
log.error("[客户端HTTP请求方法错误]", e);
return JsonResult.buildError(CommonErrorCodeEnum.CLIENT_HTTP_METHOD_ERROR);
}
/**
* 1002 客户端请求体参数校验不通过
*
* @param e
* @return
*/
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public JsonResult<Object> handle(MethodArgumentNotValidException e) {
log.error("[客户端请求体参数校验不通过]", e);
String errorMsg = this.handle(e.getBindingResult().getFieldErrors());
return JsonResult.buildError(CommonErrorCodeEnum.CLIENT_REQUEST_BODY_CHECK_ERROR.getErrorCode(), errorMsg);
}
private String handle(List<FieldError> fieldErrors) {
StringBuilder sb = new StringBuilder();
for (FieldError obj : fieldErrors) {
sb.append(obj.getField());
sb.append("=[");
sb.append(obj.getDefaultMessage());
sb.append("] ");
}
return sb.toString();
}
/**
* 1003 客户端请求体JSON格式错误或字段类型不匹配
*
* @param e
* @return
*/
@ExceptionHandler(value = HttpMessageNotReadableException.class)
public JsonResult<Object> handle(HttpMessageNotReadableException e) {
log.error("[客户端请求体JSON格式错误或字段类型不匹配]", e);
return JsonResult.buildError(CommonErrorCodeEnum.CLIENT_REQUEST_BODY_FORMAT_ERROR);
}
/**
* 1004 客户端URL中的参数类型错误
*
* @param e
* @return
*/
@ExceptionHandler(value = BindException.class)
public JsonResult<Object> handle(BindException e) {
log.error("[客户端URL中的参数类型错误]", e);
FieldError fieldError = e.getBindingResult().getFieldError();
String errorMsg = null;
if (fieldError != null) {
errorMsg = fieldError.getDefaultMessage();
if (errorMsg != null && errorMsg.contains("java.lang.NumberFormatException")) {
errorMsg = fieldError.getField() + "参数类型错误";
}
}
if (errorMsg != null && !"".equals(errorMsg)) {
return JsonResult.buildError(CommonErrorCodeEnum.CLIENT_PATH_VARIABLE_ERROR.getErrorCode(), errorMsg);
}
return JsonResult.buildError(CommonErrorCodeEnum.CLIENT_PATH_VARIABLE_ERROR);
}
/**
* 1005 客户端请求参数校验不通过
*
* @param e
* @return
*/
@ExceptionHandler(value = ConstraintViolationException.class)
public JsonResult<Object> handle(ConstraintViolationException e) {
log.error("[客户端请求参数校验不通过]", e);
Iterator<ConstraintViolation<?>> it = e.getConstraintViolations().iterator();
String errorMsg = null;
if (it.hasNext()) {
errorMsg = it.next().getMessageTemplate();
}
if (errorMsg != null && !"".equals(errorMsg)) {
return JsonResult.buildError(CommonErrorCodeEnum.CLIENT_REQUEST_PARAM_CHECK_ERROR.getErrorCode(), errorMsg);
}
return JsonResult.buildError(CommonErrorCodeEnum.CLIENT_REQUEST_PARAM_CHECK_ERROR);
}
/**
* 1006 客户端请求缺少必填的参数
*
* @param e
* @return
*/
@ExceptionHandler(value = MissingServletRequestParameterException.class)
public JsonResult<Object> handle(MissingServletRequestParameterException e) {
log.error("[客户端请求缺少必填的参数]", e);
String errorMsg = null;
String parameterName = e.getParameterName();
if (!"".equals(parameterName)) {
errorMsg = parameterName + "不能为空";
}
if (errorMsg != null) {
return JsonResult.buildError(CommonErrorCodeEnum.CLIENT_REQUEST_PARAM_REQUIRED_ERROR.getErrorCode(), errorMsg);
}
return JsonResult.buildError(CommonErrorCodeEnum.CLIENT_REQUEST_PARAM_REQUIRED_ERROR);
}
/**
* 2001 业务方法参数检查不通过
*
* @param e
* @return
*/
@ExceptionHandler(value = IllegalArgumentException.class)
public JsonResult<Object> handle(IllegalArgumentException e) {
log.error("[业务方法参数检查不通过]", e);
return JsonResult.buildError(CommonErrorCodeEnum.SERVER_ILLEGAL_ARGUMENT_ERROR);
}
/**
* 系统自定义业务异常
*
* @param e
* @return
*/
@ExceptionHandler(value = BaseBizException.class)
public JsonResult<Object> handle(BaseBizException e) {
log.error("[ 业务异常 ]", e);
return JsonResult.buildError(e.getErrorCode(), e.getErrorMsg());
}
}
DO/DTO实体类创建:
@Data
@TableName(value = "user")
public class UserDO {
@TableId(value = "id")
private Long id;
@TableField(value = "username")
private String username;
@TableField(value = "age")
private Integer age;
}
@Data
public class UserDTO {
private Long id;
private String username;
private Integer age;
}
实体类转换器,使用mapstruct来进行处理:
@Mapper(componentModel = "spring")
public interface UserConverter {
UserDTO userDO2DTO(UserDO userDO);
}
业务代码编写:
// mapper
@Mapper
public interface UserMapper extends BaseMapper<UserDO> {
}
// dao
@Slf4j
@Repository
public class UserDAO extends ServiceImpl<UserMapper, UserDO> {
@Resource
private UserMapper userMapper;
}
// service
public interface UserService {
UserDTO getById(Long id);
}
@Service
@Slf4j
public class UserServiceImpl implements UserService {
@Autowired
private UserDAO userDAO;
@Resource
private UserConverter userConverter;
@Override
public UserDTO getById(Long id) {
log.info("UserService getById id:{}", id);
UserDO userDO = userDAO.getById(id);
return userConverter.userDO2DTO(userDO);
}
}
// controller
@RestController
@RequestMapping("/user")
public class UserController {
@Resource
private UserService userService;
@GetMapping("/getUser")
public JsonResult<UserDTO> getByUser() {
UserDTO userDTO = userService.getById(1L);
return JsonResult.buildSuccess(userDTO);
}
}
然后运行项目,postman测试:
至此springboot工程搭建好了。后面会基于该工程创建多个服务项目。