@ControllerAdvice的实现主要在ExceptionHandlerExceptionResolver中
初始化
ExceptionHandlerExceptionResolve实现了InitializingBean的afterPropertiesSet接口,在这个接口中调用了initExceptionHandlerAdviceCache方法,initExceptionHandlerAdviceCache方法调用了ControllerAdviceBean类findAnnotatedBeans方法查询所有的ControllerAdvice注解,并添加到adviceBeans中,并调用了排序接口进行排序,并返回所有的注解标记的bean集合。
图1 查询所有的ControllerAdvice注解
public static List<ControllerAdviceBean> findAnnotatedBeans(ApplicationContext context) {
List<ControllerAdviceBean> adviceBeans = new ArrayList<>();
for (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context, Object.class)) {
if (!ScopedProxyUtils.isScopedTarget(name)) {
ControllerAdvice controllerAdvice = context.findAnnotationOnBean(name, ControllerAdvice.class);
if (controllerAdvice != null) {
// Use the @ControllerAdvice annotation found by findAnnotationOnBean()
// in order to avoid a subsequent lookup of the same annotation.
adviceBeans.add(new ControllerAdviceBean(name, context, controllerAdvice));
}
}
}
OrderComparator.sort(adviceBeans);
return adviceBeans;
}
ExceptionHandlerExceptionResolve的initExceptionHandlerAdviceCache方法,获取到所有的ControllerAdviceBean后,遍历并初始化ExceptionHandlerMethodResolve类,在把他们直接的关系保存在exceptionHandlerAdviceCache中
图2
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);
}
//调用查找类中包含@ExceptionHandler注解,并进行初始化,具体查看图3
ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(beanType);
if (resolver.hasExceptionMappings()) {
//存放ControllerAdviceBean与resolver映射关系,转换异常是会有用,具体查看图6
this.exceptionHandlerAdviceCache.put(adviceBean, resolver);
}
if (ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
this.responseBodyAdvice.add(adviceBean);
}
}
}
}
ExceptionHandlerMethodResolve类的初始化,查找类中包含@ExceptionHandler注解的方法,并把他们添加到mappedMethods map中
key为异常类型,value为具体method
图3
public ExceptionHandlerMethodResolver(Class<?> handlerType) {
for (Method method : MethodIntrospector.selectMethods(handlerType, EXCEPTION_HANDLER_METHODS)) {
for (Class<? extends Throwable> exceptionType : detectExceptionMappings(method)) {
addExceptionMapping(exceptionType, method);
}
}
}
private void addExceptionMapping(Class<? extends Throwable> exceptionType, Method method) {
Method oldMethod = this.mappedMethods.put(exceptionType, method);
if (oldMethod != null && !oldMethod.equals(method)) {
throw new IllegalStateException("Ambiguous @ExceptionHandler method mapped for [" +
exceptionType + "]: {" + oldMethod + ", " + method + "}");
}
}
以上步骤初始化就完成了。
异常包装处理
首先HandlerExceptionResolver类间接的实现了HandlerExceptionResolver类的resolveException接口,具体方法名为doResolveHandlerMethodException,这样当系统出现异常后,spring DispatcherServlet类的doDispatch方法会调用processDispatchResult方法,而此方法会调用resolveException接口:
DispatcherServle类 图4
@Nullable
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
@Nullable Object handler, Exception ex) throws Exception {
...
ModelAndView exMv = null;
if (this.handlerExceptionResolvers != null) {
for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
//调用resolveException接口
exMv = resolver.resolveException(request, response, handler, ex);
if (exMv != null) {
break;
}
}
}
if (exMv != null) {
....
return exMv;
}
throw ex;
}
通过调用resolveException接口调用到了ExceptionHandlerExceptionResolver的doResolveHandlerMethodException接口,此接口进行异常的包装
图5
@Override
@Nullable
protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request,
HttpServletResponse response, @Nullable HandlerMethod handlerMethod, Exception exception) {
//查找对应的异常处理方法 ,具体查看图6
ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception);
...
ServletWebRequest webRequest = new ServletWebRequest(request, response);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
try {
Throwable cause = exception.getCause();
if (cause != null) {
// 调用方法执行,把转换后的结果存放在response对象中
exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, cause, handlerMethod);
}
else {
// 调用方法执行,把转换后的结果存放在response对象中
exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, handlerMethod);
}
}
catch (Throwable invocationEx) {
....
return null;
}
if (mavContainer.isRequestHandled()) {
return new ModelAndView();
}
else {
...
return mav;
}
}
遍历ControllerAdviceBean,假如我们项目中有2个@ControllerAdvice修饰的类,而且里面申明的异常都一样,那么就看放入exceptionHandlerAdviceCache顺序了,谁进入的早,那么谁的方法就返回,具体可查看图2源码
图6
@Nullable
protected ServletInvocableHandlerMethod getExceptionHandlerMethod(
@Nullable HandlerMethod handlerMethod, Exception exception) {
...
for (Map.Entry<ControllerAdviceBean, ExceptionHandlerMethodResolver> entry : this.exceptionHandlerAdviceCache.entrySet()) {
ControllerAdviceBean advice = entry.getKey();
if (advice.isApplicableToBeanType(handlerType)) {
ExceptionHandlerMethodResolver resolver = entry.getValue();
Method method = resolver.resolveMethod(exception);
if (method != null) {
return new ServletInvocableHandlerMethod(advice.resolveBean(), method);
}
}
}
return null;
}