@ExceptionHandler原理浅析

@ExceptionHandler原理浅析

初始化

初始化 ExceptionHandlerExceptionResolver#exceptionHandlerAdviceCache
初始化方法:initExceptionHandlerAdviceCache()

   public static List<ControllerAdviceBean> findAnnotatedBeans(ApplicationContext context) {
        return Arrays.stream(BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context, Object.class))
                .filter(name -> !ScopedProxyUtils.isScopedTarget(name))
                .filter(name -> context.findAnnotationOnBean(name, ControllerAdvice.class) != null)
                .map(name -> new ControllerAdviceBean(name, context))
                .collect(Collectors.toList());
    }

advice排序

对获取的bean排序:AnnotationAwareOrderComparator#sort(adviceBeans)

寻找异常处理方法

遍历ControllerAdviceBean,寻找被@ExceptionHandler修饰的方法

   public ExceptionHandlerMethodResolver(Class<?> handlerType) {
        for (Method method : MethodIntrospector.selectMethods(handlerType, EXCEPTION_HANDLER_METHODS)) {
            for (Class<? extends Throwable> exceptionType : detectExceptionMappings(method)) {
                addExceptionMapping(exceptionType, method);
            }
        }
    }

寻找@ExceptionHandler的function

   public static final MethodFilter EXCEPTION_HANDLER_METHODS = method ->
            AnnotatedElementUtils.hasAnnotation(method, ExceptionHandler.class);

advice排序原理

ControllerAdviceBean会实现Ordered接口,ControllerAdviceBean中的order字段会取被@RestControllerAdvice修饰的对象中@Order注解中的value

默认都是最低优先级

public interface Ordered {

    /**
     * Useful constant for the highest precedence value.
     * @see java.lang.Integer#MIN_VALUE
     */
    int HIGHEST_PRECEDENCE = Integer.MIN_VALUE;

    /**
     * 低优先级
     * Useful constant for the lowest precedence value.
     * @see java.lang.Integer#MAX_VALUE
     */
    int LOWEST_PRECEDENCE = Integer.MAX_VALUE;

    /**
     * Get the order value of this object.
     * <p>Higher values are interpreted as lower priority. As a consequence,
     * the object with the lowest value has the highest priority (somewhat
     * analogous to Servlet {@code load-on-startup} values).
     * <p>Same order values will result in arbitrary sort positions for the
     * affected objects.
     * @return the order value
     * @see #HIGHEST_PRECEDENCE
     * @see #LOWEST_PRECEDENCE
     */
    int getOrder();

}

小结

使用@Order注解设置bean的优先级是有用的。

⚠️:value越小优先级越高

选择advice的逻辑

方法:ExceptionHandlerExceptionResolver#getExceptionHandlerMethod()

只要advice.isApplicableToBeanType(handlerType)是true即可

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);
      }
   }
}

根据异常匹配处理方法

private Method getMappedMethod(Class<? extends Throwable> exceptionType) {
   List<Class<? extends Throwable>> matches = new ArrayList<>();
   for (Class<? extends Throwable> mappedException : this.mappedMethods.keySet()) {
      if (mappedException.isAssignableFrom(exceptionType)) {
         matches.add(mappedException);
      }
   }
   if (!matches.isEmpty()) {
      matches.sort(new ExceptionDepthComparator(exceptionType));
     //取排序后的第一个method
      return this.mappedMethods.get(matches.get(0));
   }
   else {
      return null;
   }
}

@Exceptionhandle修饰的方法排序逻辑

递归查询exceptionhandle中的Exception类型与当前异常类型查的层级数。

总的来说就是离当前异常越近,优先级越高。

comparetor:ExceptionDepthComparator

public int compare(Class<? extends Throwable> o1, Class<? extends Throwable> o2) {
   int depth1 = getDepth(o1, this.targetException, 0);
   int depth2 = getDepth(o2, this.targetException, 0);
   return (depth1 - depth2);
}

private int getDepth(Class<?> declaredException, Class<?> exceptionToMatch, int depth) {
   if (exceptionToMatch.equals(declaredException)) {
      // Found it!
      return depth;
   }
   // If we've gone as far as we can go and haven't found it...
   if (exceptionToMatch == Throwable.class) {
      return Integer.MAX_VALUE;
   }
   return getDepth(declaredException, exceptionToMatch.getSuperclass(), depth + 1);
}

判断handlerType(controller)是否应该由当前ControllerAdvice来处理

判断ControllerAdvice的生效范围是否包含报错的controller

/**
 * Check whether the given bean type should be assisted by this
 * {@code @ControllerAdvice} instance.
 * @param beanType the type of the bean to check
 * @since 4.0
 * @see org.springframework.web.bind.annotation.ControllerAdvice
 */
public boolean isApplicableToBeanType(@Nullable Class<?> beanType) {
   return this.beanTypePredicate.test(beanType);
}
判断逻辑
private boolean hasSelectors() {
   return (!this.basePackages.isEmpty() || !this.assignableTypes.isEmpty() || !this.annotations.isEmpty());
}
这几个参数的来源

在ControllerAdviceBean的构造函数中初始化

ControllerAdvice annotation = (beanType != null ?
      AnnotatedElementUtils.findMergedAnnotation(beanType, ControllerAdvice.class) : null);

if (annotation != null) {
   this.beanTypePredicate = HandlerTypePredicate.builder()
         .basePackage(annotation.basePackages())
         .basePackageClass(annotation.basePackageClasses())
         .assignableType(annotation.assignableTypes())
         .annotation(annotation.annotations())
         .build();
原因

没有指定basePackages那么这个advice对所有类都生效。指定了要判断当前报异常的类是否在basePackages中。

总结

  1. 当classpath中有多个@RestControllerAdvice是,可以使用@Order指定顺序
  2. ExceptionHandlerExceptionResolver只会选择第一个匹配的advice执行,多余的不会执行
  3. 使用@ExceptionHandle精确指定Exception是有用的。advice会精准执行该方法
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇