spring @ControllerAdvice源码分析

@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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值