zoukankan      html  css  js  c++  java
  •  ControllerAdvice 是如何起作用的

    0. 背景

          在controller 暴露的各个接口下通常会抛出各种异常,我们希望所有的异常处理都可以收口到一处。而ControllerAdvice 这个annotation标签就是起这种作用的。那为什么只要在一个类上打上这个标签就会起作用呢。

    1. GlobalExceptionHandler示例

     1 @ControllerAdvice
     2 public class GlobalExceptionHandler {
     3     
     4     @ResponseBody
     5     @ExceptionHandler(MyExceptionA.class)
     6     public MyResponse<Object> onMyExceptionA(HttpServletRequest req, MyExceptionA e) {
     7         return MyResponse.of(e.getData(), e.getResultCode(), e.getMessage());
     8     }
     9 
    10     @ResponseBody
    11     @ExceptionHandler(MyExceptionB.class)
    12     public MyResponse<Object> onMyExceptionB(HttpServletRequest req, MyExceptionB e) {
    13         return MyResponse.of(null, e.getErrorCode(), e.getMessage());
    14     }
    15 
    16 }

    2. 顺藤摸瓜

        我们顺着ControllerAdvice这个类的使用来自底向上挖掘它是如何发生作用的。

        首先我们看ExceptionHandler这个注解类是在哪里处理的,

     我们看到了在spring.web包下的这个类 org.springframework.web.method.annotation.ExceptionHandlerMethodResolver,进一步看下它的构造函数。
     1     /**
     2      * A constructor that finds {@link ExceptionHandler} methods in the given type.
     3      * @param handlerType the type to introspect
     4      */
     5     public ExceptionHandlerMethodResolver(Class<?> handlerType) {
     6         for (Method method : MethodIntrospector.selectMethods(handlerType, EXCEPTION_HANDLER_METHODS)) {
     7             for (Class<? extends Throwable> exceptionType : detectExceptionMappings(method)) {
     8                 addExceptionMapping(exceptionType, method);
     9             }
    10         }
    11     }
    12 
    13     /**
    14      * A filter for selecting {@code @ExceptionHandler} methods.
    15      */
    16     public static final MethodFilter EXCEPTION_HANDLER_METHODS = new MethodFilter() {
    17         @Override
    18         public boolean matches(Method method) {
    19             return (AnnotationUtils.findAnnotation(method, ExceptionHandler.class) != null);
    20         }
    21     };

          从构造函数可以看出是从一个handlerType的类中取出所有被ExceptionHanlder标注的方法,存储一个<Exception, Method>的方法映射。

       

          其次我们看ExceptionHandlerMethodResolver这个类的构造函数是在哪里使用的,

          在sping.webmvc包下这个类org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver的初始化动作里,我们看到ExceptionHandlerMethodResolver构造函数的入参正是ControllerAdviceBean的BeanType, 也就是前面我们提到的GlobalExceptionHandler示例。

     1     private void initExceptionHandlerAdviceCache() {
     2         if (getApplicationContext() == null) {
     3             return;
     4         }
     5         if (logger.isDebugEnabled()) {
     6             logger.debug("Looking for exception mappings: " + getApplicationContext());
     7         }
     8 
     9         List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
    10         AnnotationAwareOrderComparator.sort(adviceBeans);
    11 
    12         for (ControllerAdviceBean adviceBean : adviceBeans) {
    13             ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(adviceBean.getBeanType());
    14             if (resolver.hasExceptionMappings()) {
    15                 this.exceptionHandlerAdviceCache.put(adviceBean, resolver);
    16                 if (logger.isInfoEnabled()) {
    17                     logger.info("Detected @ExceptionHandler methods in " + adviceBean);
    18                 }
    19             }
    20             if (ResponseBodyAdvice.class.isAssignableFrom(adviceBean.getBeanType())) {
    21                 this.responseBodyAdvice.add(adviceBean);
    22                 if (logger.isInfoEnabled()) {
    23                     logger.info("Detected ResponseBodyAdvice implementation in " + adviceBean);
    24                 }
    25             }
    26         }
    27     }

    3. 完整链条

           从应用的GlobalExceptionHandler叶子节点一直向上追溯我们追查到ExceptionHandlerExceptionResolver这个类,那完成的链路是啥呢?

           从入口 DispatcherServlet#doDispatch() -> DispatcherServlet#processDispatchResult() -> DispatcherServlet#processHandlerException() -> HandlerExceptionResolver#resolveException() -> AbstractHandlerExceptionResolver#doResolveException() -> AbstractHandlerMethodExceptionResolver#doResolveHandlerMethodException() -> ExceptionHandlerExceptionResolver#doResolveHandlerMethodException()

    -> ExceptionHandlerMethodResolver#getMappedMethod().invoke()

    以上。

  • 相关阅读:
    【转】P2P通信原理与实现(C++)
    【转】P2P通信标准协议(二)之TURN
    【转】P2P之UDP穿透NAT的原理与实现
    【转】P2P的原理和常见的实现方式
    【转】linux中man使用技巧
    【转】go编译时,加入svn版本信息
    各种移动GPU压缩纹理的使用方法
    Unity贴图压缩格式设置
    关于U3D贴图格式压缩
    可能会导致.NET内存泄露的8种行为
  • 原文地址:https://www.cnblogs.com/sinsonglew/p/11946425.html
Copyright © 2011-2022 走看看