【8】AbstractHandlerMethodExceptionResolver
AbstractHandlerMethodExceptionResolver与子类ExceptionHandlerExceptionResolver一起处理HandlerMethod在处理过程中抛出的异常。如何解析异常呢?通常是通过标注了@ExceptionHandler注解的方法处理异常。
ExceptionHandlerExceptionResolver解析器支持你添加使用自定义参数解析器和返回结果处理器,当然你也可以通过setArgumentResolvers和setReturnValueHandlers(List)方法完全自定义参数解析和返回结果处理器。
① AbstractHandlerMethodExceptionResolver
如下所示,该抽象类有三个方法:
- 重写了shouldApplyTo方法,进行了HandlerMethod判断;
- doResolveException也是个模板方法,具体逻辑交给子类处理
- doResolveHandlerMethodException是个抽象方法,由子类实现
public abstract class AbstractHandlerMethodExceptionResolver extends AbstractHandlerExceptionResolver { //重写了shouldApplyTo,这里主要检查了HandlerMethod @Override protected boolean shouldApplyTo(HttpServletRequest request, @Nullable Object handler) { if (handler == null) { return super.shouldApplyTo(request, null); } // 如果是HandlerMethod,则获取其所属bean然后调用委派给父类处理 else if (handler instanceof HandlerMethod) { HandlerMethod handlerMethod = (HandlerMethod) handler; handler = handlerMethod.getBean(); return super.shouldApplyTo(request, handler); } else if (hasGlobalExceptionHandlers() && hasHandlerMappings()) { return super.shouldApplyTo(request, handler); } else { return false; } } //此解析器是否具有全局异常处理程序,例如, //未在引发异常的{@code HandlerMethod}的同一类中声明,因此可以应用于任何处理程序。 protected boolean hasGlobalExceptionHandlers() { return false; } // 异常解析的入口方法,其也是一个模板方法,给子类实现doResolveHandlerMethodException @Override @Nullable protected final ModelAndView doResolveException( HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) { HandlerMethod handlerMethod = (handler instanceof HandlerMethod ? (HandlerMethod) handler : null); return doResolveHandlerMethodException(request, response, handlerMethod, ex); } // 异常解析核心方法,由子类实现 @Nullable protected abstract ModelAndView doResolveHandlerMethodException( HttpServletRequest request, HttpServletResponse response, @Nullable HandlerMethod handlerMethod, Exception ex); }
② ExceptionHandlerExceptionResolver
ExceptionHandlerExceptionResolver是通过解析标注了@ExceptionHandler注解的方法进行异常处理的。
如下所示,其继承自AbstractHandlerMethodExceptionResolvers实现了ApplicationContextAware、InitializingBean接口。
实现了ApplicationContextAware意味着其会拥有applicationContext引用。
实现了InitializingBean接口意味着在实例化时会调用其afterPropertiesSet方法。
① 核心属性和构造方法
// 自定义的参数解析器 @Nullable private List<HandlerMethodArgumentResolver> customArgumentResolvers; // 参数解析器的组合包装 @Nullable private HandlerMethodArgumentResolverComposite argumentResolvers; //自定义返回结果处理器 @Nullable private List<HandlerMethodReturnValueHandler> customReturnValueHandlers; // 返回结果处理器的组合包装 @Nullable private HandlerMethodReturnValueHandlerComposite returnValueHandlers; // 转换器 处理请求响应 private List<HttpMessageConverter<?>> messageConverters; // 内容协商管理器,处理MediaType private ContentNegotiationManager contentNegotiationManager = new ContentNegotiationManager(); // 维护了ResponseBodyAdvice列表 private final List<Object> responseBodyAdvice = new ArrayList<>(); @Nullable private ApplicationContext applicationContext; // 缓存,adviceBean:ExceptionHandlerMethodResolver private final Map<Class<?>, ExceptionHandlerMethodResolver> exceptionHandlerCache = new ConcurrentHashMap<>(64); // 缓存,根据ControllerAdviceBean获取对应的异常解析器 private final Map<ControllerAdviceBean, ExceptionHandlerMethodResolver> exceptionHandlerAdviceCache = new LinkedHashMap<>(); //构造器,添加了ByteArrayHttpMessageConverter、StringHttpMessageConverter //与SourceHttpMessageConverter和AllEncompassingFormHttpMessageConverter public ExceptionHandlerExceptionResolver() { this.messageConverters = new ArrayList<>(); this.messageConverters.add(new ByteArrayHttpMessageConverter()); this.messageConverters.add(new StringHttpMessageConverter()); if(!shouldIgnoreXml) { try { this.messageConverters.add(new SourceHttpMessageConverter<>()); } catch (Error err) { // Ignore when no TransformerFactory implementation is available } } this.messageConverters.add(new AllEncompassingFormHttpMessageConverter()); }
② afterPropertiesSet
该方法会在对象实例化过程中被调用。如下所示其是一个模板方法,主要由initExceptionHandlerAdviceCache(初始化获取ResponseBodyAdvice Bean)、getDefaultArgumentResolvers(获取默认的参数解析器)与getDefaultReturnValueHandlers(获取默认的返回结果处理器)组成。
@Override public void afterPropertiesSet() { // Do this first, it may add ResponseBodyAdvice beans initExceptionHandlerAdviceCache(); if (this.argumentResolvers == null) { List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers(); this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers); } if (this.returnValueHandlers == null) { List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers(); this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers); } }
① initExceptionHandlerAdviceCache
如下所示,这里的核心逻辑就是从容器中获取标注了@ControllerAdvice
注解的bean,称之为ControllerAdviceBean
。接下来会对adviceBeans
进行遍历循环:
如果new ExceptionHandlerMethodResolver(beanType)实例有标注了@ExceptionHandler注解的方法,则放入exceptionHandlerAdviceCache={adviceBean:ExceptionHandlerMethodResolver};
如果adviceBean是ResponseBodyAdvice类型,则放入responseBodyAdvice 列表中。
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); } } // 一些日志打印 }
② getDefaultArgumentResolvers
实例化一些默认参数解析器,并尝试放入用户自己定义的参数解析器
protected List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() { List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(); // Annotation-based argument resolution resolvers.add(new SessionAttributeMethodArgumentResolver()); resolvers.add(new RequestAttributeMethodArgumentResolver()); // Type-based argument resolution resolvers.add(new ServletRequestMethodArgumentResolver()); resolvers.add(new ServletResponseMethodArgumentResolver()); resolvers.add(new RedirectAttributesMethodArgumentResolver()); resolvers.add(new ModelMethodProcessor()); // Custom arguments if (getCustomArgumentResolvers() != null) { resolvers.addAll(getCustomArgumentResolvers()); } return resolvers; }
③ getDefaultReturnValueHandlers
实例化一些返回结果处理器放入List handlers
中,并尝试放入用户自定义返回结果处理器。
protected List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() { List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>(); // Single-purpose return value types handlers.add(new ModelAndViewMethodReturnValueHandler()); handlers.add(new ModelMethodProcessor()); handlers.add(new ViewMethodReturnValueHandler()); handlers.add(new HttpEntityMethodProcessor( getMessageConverters(), this.contentNegotiationManager, this.responseBodyAdvice)); // Annotation-based return value types handlers.add(new ServletModelAttributeMethodProcessor(false)); handlers.add(new RequestResponseBodyMethodProcessor( getMessageConverters(), this.contentNegotiationManager, this.responseBodyAdvice)); // Multi-purpose return value types handlers.add(new ViewNameMethodReturnValueHandler()); handlers.add(new MapMethodProcessor()); // Custom return value types if (getCustomReturnValueHandlers() != null) { handlers.addAll(getCustomReturnValueHandlers()); } // Catch-all 这个很有意思哦,将会作为复杂类型的默认处理器 handlers.add(new ServletModelAttributeMethodProcessor(true)); return handlers; }
③ 核心方法doResolveHandlerMethodException
如下所示,这里有这样几个关键步骤:
① getExceptionHandlerMethod获取ServletInvocableHandlerMethod ,获取不到直接返回null;
② exceptionHandlerMethod.invokeAndHandle进行实际处理,其实就是解析请求参数、调用目标方法以及最终处理返回结果,这块前面已经很熟悉了;
③ 对ModelAndView 进行处理;
@Override @Nullable protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerMethod handlerMethod, Exception exception) { // 获取ServletInvocableHandlerMethod ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception); if (exceptionHandlerMethod == null) {//为null就直接返回null return null; } // 设置参数解析器 if (this.argumentResolvers != null) { exceptionHandlerMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers); } // 设置返回结果处理器 if (this.returnValueHandlers != null) { exceptionHandlerMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers); } // 实例化ServletWebRequest 与ModelAndViewContainer ServletWebRequest webRequest = new ServletWebRequest(request, response); ModelAndViewContainer mavContainer = new ModelAndViewContainer(); try { if (logger.isDebugEnabled()) { logger.debug("Using @ExceptionHandler " + exceptionHandlerMethod); } // 这里首先尝试对cause 调用exceptionHandlerMethod方法 //如果cause为null,则对exception调用exceptionHandlerMethod方法 Throwable cause = exception.getCause(); if (cause != null) { // Expose cause as provided argument as well exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, cause, handlerMethod); } else { // Otherwise, just the given exception as-is exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, handlerMethod); } } catch (Throwable invocationEx) { // Any other than the original exception (or its cause) is unintended here, // probably an accident (e.g. failed assertion or the like). if (invocationEx != exception && invocationEx != exception.getCause() && logger.isWarnEnabled()) { logger.warn("Failure in @ExceptionHandler " + exceptionHandlerMethod, invocationEx); } // Continue with default processing of the original exception... return null; } // 如果请求已经被处理完毕,则直接返回空的ModelAndView if (mavContainer.isRequestHandled()) { return new ModelAndView(); } else { // 如果请求没有处理完毕,则封装ModelAndView ModelMap model = mavContainer.getModel(); HttpStatus status = mavContainer.getStatus(); ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, status); mav.setViewName(mavContainer.getViewName()); if (!mavContainer.isViewReference()) { mav.setView((View) mavContainer.getView()); } // 如果model是RedirectAttributes ,则放入outputFlashMap if (model instanceof RedirectAttributes) { Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes(); RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes); } return mav; } }
那么这里我们着重分析getExceptionHandlerMethod,我们看其是如何获取异常对应的method的。
getExceptionHandlerMethod方法简单来说就是首先从当前handlerMethod所在的类继承树中找到合适的method。如果找不到或者handlerMethod为null,则从其ControllerAdvice 类中尝试寻找合适的method。
@Nullable protected ServletInvocableHandlerMethod getExceptionHandlerMethod( @Nullable HandlerMethod handlerMethod, Exception exception) { Class<?> handlerType = null; // 如果handlerMethod 不为null if (handlerMethod != null) { // 获取handlerMethod所属的bean type handlerType = handlerMethod.getBeanType(); // 尝试从exceptionHandlerCache获取handlerType对应的ExceptionHandlerMethodResolver ExceptionHandlerMethodResolver resolver = this.exceptionHandlerCache.get(handlerType); if (resolver == null) { //如果resolver 为null,则实例化ExceptionHandlerMethodResolverb并放入exceptionHandlerCache resolver = new ExceptionHandlerMethodResolver(handlerType); this.exceptionHandlerCache.put(handlerType, resolver); } // 获取Method 然后返回ServletInvocableHandlerMethod Method method = resolver.resolveMethod(exception); if (method != null) { return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method); } // 如果是代理,则获取被代理的类型-targetClass,以便下方advice 检测 if (Proxy.isProxyClass(handlerType)) { handlerType = AopUtils.getTargetClass(handlerMethod.getBean()); } } // 如果handlerMethod为null,则从其他ControllerAdviceBean 寻找合适的method for (Map.Entry<ControllerAdviceBean, ExceptionHandlerMethodResolver> entry : this.exceptionHandlerAdviceCache.entrySet()) { ControllerAdviceBean advice = entry.getKey(); // 如果当前advice 适用于handlerType if (advice.isApplicableToBeanType(handlerType)) { // 获取resolverf然后解析得到method ExceptionHandlerMethodResolver resolver = entry.getValue(); Method method = resolver.resolveMethod(exception); if (method != null) { return new ServletInvocableHandlerMethod(advice.resolveBean(), method); } } } return null; }
代码逻辑总结如下:
- ① 如果handlerMethod 不为null:
② 获取handlerMethod所属的bean type,根据bean type尝试从exceptionHandlerCache获取handlerType对应的ExceptionHandlerMethodResolver
③ 如果获取不到resolver就new一个ExceptionHandlerMethodResolver 然后放入exceptionHandlerCache
④ resolver.resolveMethod(exception);获取异常对应的method,如果不为null直接返回new ServletInvocableHandlerMethod(handlerMethod.getBean(), method);
⑤ 如果handlerType是代理,则获取其targetClass,以便下面advice使用
⑥ 如果handlerMethod为null或者上面没有获取到合适的method,则遍历exceptionHandlerAdviceCache中的ControllerAdviceBean,尝试找到一个合适的method
⑦ 如果最终得不到method,则返回null。
【9】 ExceptionHandlerMethodResolver
上面我们在遍历List adviceBeans
提到了ExceptionHandlerMethodResolver
,那么这里我们见到看一下这个解析器。
① 核心属性和构造方法
// 一个方法过滤器,筛选标注了@ExceptionHandler注解的方法 public static final MethodFilter EXCEPTION_HANDLER_METHODS = method -> AnnotatedElementUtils.hasAnnotation(method, ExceptionHandler.class); // 维护了异常:Method的map ,初始大小是16 private final Map<Class<? extends Throwable>, Method> mappedMethods = new HashMap<>(16); // 缓存,便于快捷查找异常对应的method private final Map<Class<? extends Throwable>, Method> exceptionLookupCache = new ConcurrentReferenceHashMap<>(16); // 核心构造器,mappedMethods 就是在这里初始化。 // 1.筛选标注了@ExceptionHandler注解的方法,然后遍历方法 //2.获取方法中注解标注的异常映射,遍历放入mappedMethods中 public ExceptionHandlerMethodResolver(Class<?> handlerType) { for (Method method : MethodIntrospector.selectMethods(handlerType, EXCEPTION_HANDLER_METHODS)) { for (Class<? extends Throwable> exceptionType : detectExceptionMappings(method)) { addExceptionMapping(exceptionType, method); } } }
②获取method
如下所示其获取异常对应的method是一个递归方法,如果不能直接根据异常类型获取到对应的method,那么就会获取其cause,然后根据cause再次递归自身。
//resolveMethod仅仅只是调用了resolveMethodByThrowable @Nullable public Method resolveMethod(Exception exception) { return resolveMethodByThrowable(exception); } @Nullable public Method resolveMethodByThrowable(Throwable exception) { Method method = resolveMethodByExceptionType(exception.getClass()); if (method == null) { Throwable cause = exception.getCause(); if (cause != null) { method = resolveMethodByThrowable(cause); } } return method; }
resolveMethodByExceptionType
方法如下所示,首先从exceptionLookupCache
获取异常对应的Method ,获取不到则调用getMappedMethod
方法获取,然后放入到exceptionLookupCache
中。
@Nullable public Method resolveMethodByExceptionType(Class<? extends Throwable> exceptionType) { Method method = this.exceptionLookupCache.get(exceptionType); if (method == null) { method = getMappedMethod(exceptionType); this.exceptionLookupCache.put(exceptionType, method); } return method; }
getMappedMethod
核心逻辑则是获取mappedMethods
所有key,如果key是exceptionType
类型则放入matches中,最后对matches进行排序返回第一个元素。
@Nullable 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)); return this.mappedMethods.get(matches.get(0)); } else { return null; } }