Controller:
@RestController
public class ExceptionController {
@RequestMapping("/exception")
public void processException(){
throw new MyException(100,"出错了");
}
}
自定义异常类:
@Data
public class MyException extends RuntimeException {
private Integer code;
private String msg;
public MyException(String message) {
super(message);
}
public MyException(Integer code,String message) {
super(message);
this.code = code;
}
}
我们看这个服务返回信息,这个是系统默认返回的,这些信息有时候没法给用户一个好的提示
所以我们要自定义异常处理,自定义设置返回信息,并且按照统一格式进行返回
@Order(Ordered.HIGHEST_PRECEDENCE)
@RestControllerAdvice
@Slf4j
public class MyExceptionHandler {
@ExceptionHandler(value = Exception.class)
public ResponseData baseErrorHandler(HttpServletRequest req, Exception e) throws Exception {
ResponseData result;
if (MyException.class.isAssignableFrom(e.getClass())) {
MyException applicationException = (MyException) e;
result = new ResponseData(applicationException.getCode(),applicationException.getMsg());
result.setMsg(applicationException.getMessage());
} else {
result = new ResponseData();
result.setMsg(e.getMessage());
}
return result;
}
}
自定义异常处理统一返回的格式
@Data
public class ResponseData {
private Integer code;
private String msg;
public ResponseData() {}
public ResponseData(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
}
现在来看看 @RestControllerAdvice的源码分析:
public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExceptionResolver
implements ApplicationContextAware, InitializingBean {
........................
private final Map<ControllerAdviceBean, ExceptionHandlerMethodResolver> exceptionHandlerAdviceCache =
new LinkedHashMap<>();
........................
}
ExceptionHandlerExceptionResolver 实现了InitializingBean 接口,说明其类中的afterPropertiesSet方法要在初始化bean完成之后进行调用,并且可以获取所有的单例bean
再看看afterPropertiesSet方法:
@Override
public void afterPropertiesSet() {
// Do this first, it may add ResponseBodyAdvice beans
initExceptionHandlerAdviceCache();
.......................
}
private void initExceptionHandlerAdviceCache() {
if (getApplicationContext() == null) {
return;
}
List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
for (ControllerAdviceBean adviceBean : adviceBeans) {
Class<?> beanType = adviceBean.getBeanType();
if (beanType == null) {
throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);
}
ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(beanType);
if (resolver.hasExceptionMappings()) {
this.exceptionHandlerAdviceCache.put(adviceBean, resolver);
}
if (ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
this.responseBodyAdvice.add(adviceBean);
}
}
if (logger.isDebugEnabled()) {
int handlerSize = this.exceptionHandlerAdviceCache.size();
int adviceSize = this.responseBodyAdvice.size();
if (handlerSize == 0 && adviceSize == 0) {
logger.debug("ControllerAdvice beans: none");
}
else {
logger.debug("ControllerAdvice beans: " +
handlerSize + " @ExceptionHandler, " + adviceSize + " ResponseBodyAdvice");
}
}
}
其大概的意思就是找出@ControllerAdvice注解修饰的类,并且放到this.exceptionHandlerAdviceCache这样一个hashMap中
所以服务启动后,在ExceptionHandlerExceptionResolver 的单例bean中exceptionHandlerAdviceCache 属性就会初始化好,再看看什么时候用到 这个属性的呢?
当我们请求服务:http://localhost:4449/exception,这个必然会走DispatcherServlet中的doDispatch方法
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
............................
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
............................
}
在doDispatch,会执行对mv和异常进行处理,主要是spring支持扩展类对结果返回进行处理
看参数中mv为null,异常为自定义的异常类
所以最终的异常处理,会去调用MyExceptionHandler中的baseErrorHandler方法进行处理,由于baseErrorHandler方法被@ResponseBody修饰,最终还是以model的形式返回