@ControllerAdvice 和 @ExceptionHandler注解处理全局异常
前言:开发过程中,难免有的程序会因为某些原因抛出异常,而这些异常一般都是利用try ,catch的方式处理异常或者throw,throws的方式抛出异常不管。这种方法对于程序员来说处理也比较麻烦,对客户来说也不太友好,所以我们希望既能方便程序员编写代码,不用过多的自己去处理各种异常编写重复的代码又能提升用户的体验,这时候全局异常处理就显得很重要也很便捷了。
一@ControllerAdvice和@ExceptionHandler简介
在构建RestFul接口的今天,我们一般会限定好返回数据的格式,有利于前端调用解析,比如:
{
"status": 1001,
"message": "接口执行成功",
"result": [
{
"id": 1,
"username": "root",
"password": "123456",
"authority": "admin"
}
]
}
但有时却往往会产生一些bug,这时候就破坏了返回数据的一致性,导致调用者无法解析。所以我们常常会定义一个全局的异常拦截器
1.1 @ControllerAdvice
- @ControllerAdvice 是Spring 3.2提供的新注解,可以对Controller中使用到@RequestMapping注解的方法做逻辑处理。
@ControllerAdvice ,很多初学者可能都没有听说过这个注解,实际上这是一个非常有用的注解。顾名思义,这是一个增强的 Controller,一般配合@ExceptionHandler使用来处理全局异常。注意不能自己try和catch异常,否则就不会被全局异常处理捕获到。
使用这个注解 ,可以实现三个方面的功能:
-
全局异常处理
-
全局数据绑定
-
全局数据预处理
1.2 @ExceptionHandler
-
说明捕获那些异常,对那些异常进行处理
@ExceptionHandler(NullPointerException.class) //只处理空指针异常 @ExceptionHandler(Exception.class) //处理全部异常
二 ResponseBodyAdvice 接口
实现ResponseBodyAdvice接口,其实是对加了@RestController(也就是@Controller+@ResponseBody)注解的处理器将要返回的值进行增强处理。
其实也就是采用了AOP的思想,对返回值进行一次修改。
-
个人理解
ResponseBodyAdvice 接口是在Controller执行之后,在response返回给客户端之前,执行的对response的一些处理,可以实现对response数据的一些统一封装或者加密等操作
2.1接口中的两个 方法
- supports - 判断是否要执行beforeBodyWrite 方法,true执行 false 不执行 用于选择那些满足条件的类或者方法要处理 其余的则不处理
- beforeBodyWrite 对 response 处理的具体方法
三 返回类型包装 和全局异常处理 示例
@ControllerAdvice
public class ResponseResultHandle implements ResponseBodyAdvice<Object> {
private final Logger log = LoggerFactory.getLogger(ResponseResultHandle.class);
//是否请求包含了包装注解标记,没有就直接返回,不需要重写返回体
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
//获取请求属性
ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
assert sra != null;
//获取请求
HttpServletRequest request = sra.getRequest();
//判断请求是否有包装标记 ----此处为自定义注解包装那些类
return ResponseURICache.getInstance().get(request.getRequestURI());
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
//进入返回体重写格式
if (body instanceof Result) {
//返回值异常,作包装处理
Result errorResult = (Result) body;
return BaseResponse.failure(errorResult.getStatus(), errorResult.getMessage(), errorResult.getObject());
}
//返回类型是不是异常数字
if (body instanceof Integer) {
//返回值异常,作包装处理
Integer res = (Integer) body;
if (res.equals(ResultStatus.RESULT_STATUS_1001)) {
return BaseResponse.failure(ResultStatus.RESULT_STATUS_1002);
}
}
//返回成功 自动包装类型 BaseResponse 是自己写的返回包装类
return BaseResponse.success(body);
}
//处理全部异常 里面怎么处理各种异常 自己定义
@ExceptionHandler(Exception.class)
@ResponseBody
public Result handleRuntimeException(Exception e, HttpServletRequest request, HttpServletResponse response) {
// 业务异常处理
Result errorResult = new Result();
//通用异常处理 运行时异常
if (e instanceof BizException) {
BizException bizException = (BizException) e;
log.info("BizException: " + e.getMessage());
errorResult.setStatus(bizException.getStatus());
errorResult.setMessage(e.getMessage());
return errorResult;
} else { // 全局异常包装为服务端异常
if (e instanceof ProtoException) {
log.warn("request:{},message:{}", request.getServletPath(), e.getMessage());
ProtoException protoException = (ProtoException) e;
response.setStatus(protoException.getCode());
errorResult.setStatus(ResultStatus.RESULT_STATUS_9001.getStatus());
errorResult.setMessage(e.getMessage());
errorResult.setObject(errorResult.getObject());
return errorResult;
} else if (e instanceof BindException) {
log.error(String.format("request:%s,message:%s", request.getServletPath(), e.getMessage()), e);
BindingResult bindingResult = ((BindException) e).getBindingResult();
// 服务端异常
List<String> errMsgList = new ArrayList<String>();
for (ObjectError error : bindingResult.getAllErrors()) {
errMsgList.add(error.getDefaultMessage());
}
errorResult.setStatus(ResultStatus.RESULT_STATUS_1002.getStatus());
errorResult.setMessage(errMsgList.toString());
errorResult.setObject(null);
return errorResult;
}
// ConstraintViolationException
// else if (e instanceof ConstraintViolationException) {
log.error(String.format("request:%s,message:%s", request.getServletPath(), e.getMessage()), e);
// errorResult.setStatus(ResultStatus.RESULT_STATUS_1002.getStatus());
// errorResult.setMessage(e.getMessage());
// errorResult.setObject(null);
// return errorResult;
// }
else if (e instanceof MethodArgumentNotValidException) {
// log.error(String.format("request:%s,message:%s", request.getServletPath(), e.getMessage()), e);
BindingResult bindingResult = ((MethodArgumentNotValidException) e).getBindingResult();
Object requestArg = bindingResult.getTarget();
if (requestArg != null) {
// log.error("参数上报错误,接口:{},上报参数:{}", request.getServletPath(), requestArg.toString());
}
// 服务端异常
Set<String> err=new HashSet<>();
for (ObjectError error : bindingResult.getAllErrors()) {
err.add(error.getDefaultMessage());
}
errorResult.setStatus(ResultStatus.RESULT_STATUS_1002.getStatus());
errorResult.setMessage(err.toString());
errorResult.setObject(null);
return errorResult;
} else {
log.error(String.format("request:%s,message:%s", request.getServletPath(), e.getMessage()), e);
System.out.println("request:%s,message:%s" + request.getServletPath() + "异常" + e.getMessage());
// 服务端异常
errorResult.setStatus(ResultStatus.RESULT_STATUS_9001.getStatus());
errorResult.setMessage(ResultStatus.RESULT_STATUS_9001.getMsg());
errorResult.setObject(errorResult.getObject());
return errorResult;
}
}
}
}