项目父工程及其子模块

版本控制

在父工程的pom中添加版本依赖信息。基础配置,后面会陆续完善

pom.xml
<properties>
<java.version>11</java.version>
<spring.boot.version>2.7.8</spring.boot.version>
<reflections.version>0.9.11</reflections.version>
<fastjson.version>2.0.32</fastjson.version>
<leadnew-common.version>0.0.1-SNAPSHOT</leadnew-common.version>
<leadnew-model.version>0.0.1-SNAPSHOT</leadnew-model.version>
<spring.cloud.version>Hoxton.SR10</spring.cloud.version>
</properties>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.swx</groupId>
<artifactId>leadnews-common</artifactId>
<version>${leadnew-common.version}</version>
</dependency>
<dependency>
<groupId>com.swx</groupId>
<artifactId>leadnews-model</artifactId>
<version>${leadnew-model.version}</version>
</dependency>

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- Spring Boot相关 -->
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>${reflections.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
<version>${spring.boot.version}</version>
</dependency>
<!-- fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
</dependencies>
</dependencyManagement>

异常和返回结果统一处理

添加依赖信息

leadnews-common模块中添加如下的依赖信息

pom.xml
<dependencies>
<dependency>
<groupId>com.swx</groupId>
<artifactId>leadnews-model</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
</dependency>
</dependencies>

leadnews-model模块中添加如下的依赖信息

pom.xml
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>

创建实体类和枚举类

leadnews-model模块中创建包com.swx.model.common.dtocom.swx.model.common.enums

创建消息提示枚举

enums.ResultCodeEnum
public enum ResultCodeEnum {

/* 成功状态码 */
SUCCESS(20000, "成功"),
/* Token 50~100 */
TOKEN_INVALID(50, "无效TOKEN"),
TOKEN_EXPIRE(51, "TOKEN已过期"),
TOKEN_REQUIRE(50, "TOKEN是必须的"),
/* 参数错误 500~1000 */
SERVER_ERROR(500, "服务器内部错误"),
PARAM_REQUIRE(501, "缺少参数"),
PARAM_INVALID(502, "无效参数"),
PARAM_TIMAGE_FORMAT_ERROR(503, "图片格式有误"),
/* 数据错误 1000~2000 */
DATA_EXIST(1000, "数据已经存在"),
AP_USER_DATA_NOT_EXIST(1001, "ApUser数据不存在"),
DATA_NOT_EXIST(1002, "数据不存在"),
/* 数据错误 300~3500 */
NO_OPERATOR_AUTH(3000, "无权操作"),
NEED_ADMIN(3001, "需要管理员权限");

private Integer code;
private String message;

private ResultCodeEnum(Integer code, String message) {
this.code = code;
this.message = message;
}

public Integer code() {
return this.code;
}
public String message() {
return this.message;
}
}

创建返回结果实体类

dto.R
/**
* 返回结果实体类
*/
@Data
public class R implements Serializable {

private static final long serialVersionUID = 1L;

/**
* 返回码
*/
private Integer code;

/**
* 返回消息
*/
private String message;

/**
* 返回数据
*/
private Object data;

public R() {

}

public R(ResultCodeEnum resultCode, Object data) {
this.code = resultCode.code();
this.message = resultCode.message();
this.data = data;
}

private void setResultCode(ResultCodeEnum resultCode) {
this.code = resultCode.code();
this.message = resultCode.message();
}

// 返回成功
public static R success() {
R result = new R();
result.setResultCode(ResultCodeEnum.SUCCESS);
return result;
}
// 返回成功
public static R success(Object data) {
R result = new R();
result.setResultCode(ResultCodeEnum.SUCCESS);
result.setData(data);
return result;
}

// 返回失败
public static R fail(Integer code, String message) {
R result = new R();
result.setCode(code);
result.setMessage(message);
return result;
}
// 返回失败
public static R fail(ResultCodeEnum resultCode) {
R result = new R();
result.setResultCode(resultCode);
return result;
}
}

创建返回分页结果实体类

dto.PageR
/**
* 返回分页结果实体类
*/
@Data
public class PageR extends R implements Serializable {
private static final long serialVersionUID = 1L;

private Integer current;
private Integer size;
private Integer total;

public PageR() {

}

public PageR(Integer current, Integer size, Integer total) {
this.current = current;
this.size = size;
this.total = total;
}
}

创建错误结果实体类

dto.ErrorResult
/**
* 异常结果包装类
* @author sw-code
*
*/
public class ErrorResult {

private Integer code;

private String message;

private String exception;

public Integer getCode() {
return code;
}

public void setCode(Integer code) {
this.code = code;
}

public String getMessage() {
return message;
}

public void setMessage(String message) {
this.message = message;
}

public String getException() {
return exception;
}

public void setException(String exception) {
this.exception = exception;
}

public static ErrorResult fail(ResultCodeEnum resultCode, Throwable e, String message) {
ErrorResult errorResult = ErrorResult.fail(resultCode, e);
errorResult.setMessage(message);
return errorResult;
}

public static ErrorResult fail(ResultCodeEnum resultCode, Throwable e) {
ErrorResult errorResult = new ErrorResult();
errorResult.setCode(resultCode.code());
errorResult.setMessage(resultCode.message());
errorResult.setException(e.getClass().getName());
return errorResult;
}
public static ErrorResult fail(Integer code, String message) {
ErrorResult errorResult = new ErrorResult();
errorResult.setCode(code);
errorResult.setMessage(message);
return errorResult;
}
}

创建自定义异常类

dto.BizException
public class BizException extends RuntimeException {

/**
* 错误码
*/
private Integer code;

/**
* 错误信息
*/
private String message;

public BizException() {
super();
}

public BizException(ResultCodeEnum resultCode) {
super(resultCode.message());
this.code = resultCode.code();
this.message = resultCode.message();
}

public BizException(ResultCodeEnum resultCode, Throwable cause) {
super(resultCode.message(), cause);
this.code = resultCode.code();
this.message = resultCode.message();
}

public BizException(String message) {
super(message);
this.code = -1;
this.message = message;
}

public BizException(Integer code, String message) {
super(message);
this.code = code;
this.message = message;
}

public BizException(Integer code, String message, Throwable cause) {
super(message, cause);
this.code = code;
this.message = message;
}

@Override
public synchronized Throwable fillInStackTrace() {
return this;
}

public Integer getCode() {
return code;
}

public void setCode(Integer code) {
this.code = code;
}

@Override
public String getMessage() {
return message;
}

public void setMessage(String message) {
this.message = message;
}
}

创建自定义注解

使用注解的Controller自动统一封装

leadnews-common模块中创建com.swx.common.exception,并在该包下创建annotation

创建自定义注解类

annotation.ResponseResult
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD})
@Documented
public @interface ResponseResult {

}

创建拦截器

com.swx.common.exception下创建interceptor

拦截器会拦截添加了自定义注解的类或者方法,完成统一封装

interceptor.ResponseResultInterceptor
@Component
public class ResponseResultInterceptor implements HandlerInterceptor {
//标记名称
public static final String RESPONSE_RESULT_ANN = "RESPONSE-RESULT-ANN";

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// TODO Auto-generated method stub
if (handler instanceof HandlerMethod) {
final HandlerMethod handlerMethod = (HandlerMethod) handler;
final Class<?> clazz = handlerMethod.getBeanType();
final Method method = handlerMethod.getMethod();
// 判断是否在类对象上添加了注解
if (clazz.isAnnotationPresent(ResponseResult.class)) {
// 设置此请求返回体,需要包装,往下传递,在ResponseBodyAdvice接口进行判断
request.setAttribute(RESPONSE_RESULT_ANN, clazz.getAnnotation(ResponseResult.class));
} else if (method.isAnnotationPresent(ResponseResult.class)) {
request.setAttribute(RESPONSE_RESULT_ANN, method.getAnnotation(ResponseResult.class));
}
}
return true;
}
}

注册拦截器

com.swx.common.exception下创建config

config.WebAppConfig
@Configuration
public class WebAppConfig implements WebMvcConfigurer {

// SpringMVC 需要手动添加拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
ResponseResultInterceptor interceptor = new ResponseResultInterceptor();
registry.addInterceptor(interceptor);
WebMvcConfigurer.super.addInterceptors(registry);
}
}

异常和返回值处理类

com.swx.common.exception下创建handler

创建全局异常处理类

handler.GlobalExceptionHandler
/**
* 全局异常处理类
* @RestControllerAdvice(@ControllerAdvice),拦截异常并统一处理
* @author sw-code
*
*/
@RestControllerAdvice
public class GlobalExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger("GlobalExceptionHandler.class");

/**
* 处理自定义的业务异常
* @param e 异常对象
* @param request request
* @return 错误结果
*/
@ExceptionHandler(BizException.class)
public ErrorResult bizExceptionHandler(BizException e, HttpServletRequest request) {
logger.error("发生业务异常!原因是: {}", e.getMessage());
return ErrorResult.fail(e.getCode(), e.getMessage());
}

// 拦截抛出的异常,@ResponseStatus:用来改变响应状态码
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(Throwable.class)
public ErrorResult handlerThrowable(Throwable e, HttpServletRequest request) {
logger.error("发生未知异常!原因是: ", e);
ErrorResult error = ErrorResult.fail(ResultCodeEnum.SERVER_ERROR, e);
return error;
}

// 参数校验异常
@ExceptionHandler(BindException.class)
public ErrorResult handleBindExcpetion(BindException e, HttpServletRequest request) {
logger.error("发生参数校验异常!原因是:",e);
ErrorResult error = ErrorResult.fail(ResultCodeEnum.PARAM_INVALID, e, e.getAllErrors().get(0).getDefaultMessage());
return error;
}

@ExceptionHandler(MethodArgumentNotValidException.class)
public ErrorResult handleMethodArgumentNotValidException(MethodArgumentNotValidException e, HttpServletRequest request) {
logger.error("发生参数校验异常!原因是:",e);
ErrorResult error = ErrorResult.fail(ResultCodeEnum.PARAM_INVALID,e,e.getBindingResult().getAllErrors().get(0).getDefaultMessage());
return error;
}
}

创建返回值处理类

handler.ResponseResultHandler
/**
* 使用 @ControllerAdvice & ResponseBodyAdvice
* 拦截Controller方法默认返回参数,统一处理返回值/响应体
*/
@ControllerAdvice
public class ResponseResultHandler implements ResponseBodyAdvice<Object> {

// 标记名称
public static final String RESPONSE_RESULT_ANN = "RESPONSE-RESULT-ANN";


// 判断是否要执行 beforeBodyWrite 方法,true为执行,false不执行,有注解标记的时候处理返回值
@Override
public boolean supports(MethodParameter arg0, Class<? extends HttpMessageConverter<?>> arg1) {
ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = sra.getRequest();
// 判断请求是否有包装标记
ResponseResult responseResultAnn = (ResponseResult) request.getAttribute(RESPONSE_RESULT_ANN);
return responseResultAnn == null ? false : true;
}


// 对返回值做包装处理,如果属于异常结果,则需要再包装
@Override
public Object beforeBodyWrite(Object body, MethodParameter arg1, MediaType arg2,
Class<? extends HttpMessageConverter<?>> arg3, ServerHttpRequest arg4, ServerHttpResponse arg5) {
if (body instanceof ErrorResult) {
ErrorResult error = (ErrorResult) body;
return R.fail(error.getCode(), error.getMessage());
} else if (body instanceof R) {
return (R) body;
} else if (body instanceof String) {
return body;
}
return R.success(body);
}
}

自动装配

leadnews-common模块的resources包下创建META-INF目录,目录中创建spring.factories文件,内容如下,当其他模块引入该模块时,会自动装配指定的类

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.swx.common.exception.config.WebAppConfig, \
com.swx.common.exception.handler.GlobalExceptionHandler, \
com.swx.common.exception.handler.ResponseResultHandler

测试

leadnews-service模块中添加如下依赖信息

pom.xml
<dependencies>
<dependency>
<groupId>com.swx</groupId>
<artifactId>leadnews-common</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>

创建com.swx.service包,并在该包下创建启动类

ServiceApplication
@SpringBootApplication
public class ServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceApplication.class, args);
}
}

创建controller包,并创建测试Controller,内容如下:

controller.TestController
@ResponseResult
@RestController
@RequestMapping("/test")
public class TestController {

@GetMapping("")
public Map<String, String> test() {
HashMap<String, String> data = new HashMap<>();
data.put("data", "01");
return data;
}
}
  • 使用了自定义的@ResponseResult注解,该类下的方法返回值会被统一处理

使用浏览器访问:http://localhost:8080/test

{"code":20000,"message":"成功","data":{"data":"01"}}