Advice类
- 由
@ControllerAdvice
和 多个@ExceptionHandler
组成 @ControllerAdvice
只作用于 控制器 相关的功能。默认是全局应用。- 通过
@ControllerAdvice
的属性value / basePackages = {"com.didi.controller"}
代表范围限定在某个包,basePackageClasses
是前面的类型安全写法,表示类所在的包。 - 通过
@ControllerAdvice
的属性assignableTypes
代表范围限定在一个数组中的类。 - 通过
@ControllerAdvice
的属性annotations
代表范围限定在对应注解标记的类中。 - 多个
@ControllerAdvice
属性应用 OR 逻辑 @ExceptionHandler
的value
代表对应的异常类型,等同于catch
。如果没有@ControllerAdvice
则只处理本类中关于 servlet 的异常。(如 ServletRequest / HttpServletRequest / HttpSession(会话对象))
@Slf4j
@RestControllerAdvice
@Order(1) // 越小越优先
public class BizExceptionHandler {
public static final String PACKAGE_NAME = "com.didi";
@ExceptionHandler(value = BizException.class)
public Result<?> handleBizException(BizException e){
log.error("业务异常: {}", getStackTraceByPackage(e, PACKAGE_NAME));
return Result.error(e.getCode(), e.getMsg());
}
@ExceptionHandler(value = Exception.class)
public Result<?> handleException(Exception e){
log.error("系统异常: {}", getStackTraceByPackage(e, PACKAGE_NAME));
return Result.error(e.getMessage());
}
public static String getStackTraceByPackage(Throwable e, String packagePrefix){
StringBuilder sb = new StringBuilder("\n").append(e);
// 仅筛选改所选包下面异常
for(StackTraceElement stackTraceElement : e.getStackTrace()){
if(!stackTraceElement.getClassName().startsWith(packagePrefix)){
continue;
}
sb.append("\n\tat ").append(stackTraceElement);
}
return sb.toString();
}
}
异常类
@Data
public class BizException extends RuntimeException {
private Integer code = 999;
private String msg;
public BizException(Integer code, String msg) {
super(msg);
this.code = code;
this.msg = msg;
}
public BizException(String msg) {
super(msg);
this.msg = msg;
}
}
断言类
工具类: 使用抽象类 abstract
( 常量类: final + private constructor, 或者直接使用枚举 )
public abstract class BizAssert {
/** 工具类(仅含有static成员 和 常量)需要进行「final+私有化构造器」 */
private BizAssert() {}
public static final String UN_CHOOSE_ARG = "未选中数据";
public static void notNull(Object pojo, String msg) {
if (pojo == null) {
throw new BizException(msg);
}
}
public static void notNull(Object pojo) {
notNull(pojo, UN_CHOOSE_ARG);
}
public static void notEmpty(Object target, String msg) {
if (isEmpty(target)) {
throw new BizException(msg);
}
}
public static void notEmpty(Object pojo) {
notEmpty(pojo, UN_CHOOSE_ARG);
}
public static boolean isEmpty(Object obj) {
if (obj == null) {
return true;
}
if (obj instanceof Optional) {
return !((Optional<?>) obj).isPresent();
}
if (obj instanceof CharSequence) {
return ((CharSequence) obj).length() == 0;
}
if (obj.getClass().isArray()) {
return Array.getLength(obj) == 0;
}
if (obj instanceof Collection) {
return ((Collection<?>) obj).isEmpty();
}
if (obj instanceof Map) {
return ((Map<?, ?>) obj).isEmpty();
}
return false;
}
}
Advice类·补充
@ControllerAdvice 其实就是一个作用于某个 范围 且专门 增强 Controller 功能的工具。使用 ControllerAdvice, 主要实现三个方面的功能:
- 全局异常处理
- 全局数据绑定
- 全局数据处理
RequestBodyAdviceAdapter
: 对所有请求进行后续处理
@ControllerAdvice
public class PrintRequestBodyAdviceAdapter extends RequestBodyAdviceAdapter {
@Override
public boolean supports(MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
return true;
}
@Override
public Object afterBodyRead(Object body, HttpInputMessage inputMessage,MethodParameter parameter, Type targetType,
Class<? extends HttpMessageConverter<?>> converterType) {
System.out.println("print request body in advice:" + body);
return super.afterBodyRead(body, inputMessage, parameter, targetType, converterType);
}
}
工作原理
当一个 Body 被解析出来后,会调用 getAdvice() 来获取 RequestResponseBodyAdviceChain
此时 Body 已经解析完毕了,传递给 PrintRequestBodyAdviceAdapter
的是一个解析过的对象,不再是一个流
代码: AbstractMessageConverterMethodArgumentResolver#readWithMessageConverters
protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType){
// ...
if (message.hasBody()) {
HttpInputMessage msgToUse = getAdvice().beforeBodyRead(message, parameter, targetType, converterType);
//读取请求中的body数据流,并转化
body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :
((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));
//转换完,找到对应的Advice,并执行他的afterBodyRead()
body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);
// ...
}
// ...
return body;
}
补充 Result 类
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.ToString;
import java.io.Serializable;
@Getter
@ToString
@ApiModel(value = "接口返回对象", description = "接口返回对象")
public class Result<T> implements Serializable {
public static final String OPS_SUCCESS = "操作成功!";
private static final long serialVersionUID = 1L;
/**
* 时间戳
*/
@ApiModelProperty("时间戳")
private final long timestamp = System.currentTimeMillis();
/**
* 成功标志
*/
@ApiModelProperty("成功标志")
private Boolean success = true;
/**
* 返回处理消息
*/
@ApiModelProperty("返回处理消息")
private String message = OPS_SUCCESS;
/**
* 返回代码
*/
@ApiModelProperty("返回代码")
private Integer code = 0;
/**
* 返回数据对象 data
*/
@ApiModelProperty("返回数据对象")
private T data;
public Result() {
}
public static <T> Result<T> ok(T data) {
return ok("成功", data);
}
public static <T> Result<T> ok() {
return ok("成功");
}
public static <T> Result<T> ok(String msg) {
return ok(msg, null);
}
public static <T> Result<T> ok(String msg, T data) {
Result<T> r = new Result<>();
r.setMessage(msg);
r.setCode(200);
r.setData(data);
return r;
}
public static <T> Result<T> error(String msg) {
return error(500, msg);
}
public static <T> Result<T> error(int code, String msg) {
return error(code, msg, null);
}
public static <T> Result<T> error(String msg, T data) {
return error(500, msg, data);
}
public static <T> Result<T> error(int code, String msg, T data) {
Result<T> r = new Result<>();
r.setSuccess(false);
r.setMessage(msg);
r.setCode(code);
r.setData(data);
return r;
}
public void setSuccess(Boolean success) {
this.success = success;
}
public void setMessage(String message) {
this.message = message;
}
public void setCode(Integer code) {
this.code = code;
}
public void setData(T data) {
this.data = data;
}
}