当前位置:   article > 正文

Spring MVC 源码- HandlerExceptionResolver 组件_org.springframework.web.servlet.mvc.support.defaul

org.springframework.web.servlet.mvc.support.defaulthandlerexceptionresolver

HandlerExceptionResolver 组件

HandlerExceptionResolver 组件,处理器异常解析器,将处理器( handler )执行时发生的异常(也就是处理请求,执行方法的过程中)解析(转换)成对应的 ModelAndView 结果

回顾

先来回顾一下在 DispatcherServlet 中处理请求的过程中哪里使用到 HandlerExceptionResolver 组件,可以回到《一个请求响应的旅行过程》中的 DispatcherServletprocessHandlerException 方法中看看,如下:

  1. @Nullable
  2. protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
  3. @Nullable Object handler, Exception ex) throws Exception {
  4. // Success and error responses may use different content types
  5. // 移除 PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE 属性
  6. request.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
  7. // Check registered HandlerExceptionResolvers...
  8. // <a> 遍历 HandlerExceptionResolver 数组,解析异常,生成 ModelAndView 对象
  9. ModelAndView exMv = null;
  10. if (this.handlerExceptionResolvers != null) {
  11. // 遍历 HandlerExceptionResolver 数组
  12. for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
  13. // 解析异常,生成 ModelAndView 对象
  14. exMv = resolver.resolveException(request, response, handler, ex);
  15. // 生成成功,结束循环
  16. if (exMv != null) {
  17. break;
  18. }
  19. }
  20. }
  21. // <b> 情况一,生成了 ModelAndView 对象,进行返回
  22. if (exMv != null) {
  23. // ModelAndView 对象为空,则返回 null
  24. if (exMv.isEmpty()) {
  25. request.setAttribute(EXCEPTION_ATTRIBUTE, ex);
  26. return null;
  27. }
  28. // We might still need view name translation for a plain error model...
  29. // 没有视图则设置默认视图
  30. if (!exMv.hasView()) {
  31. String defaultViewName = getDefaultViewName(request);
  32. if (defaultViewName != null) {
  33. exMv.setViewName(defaultViewName);
  34. }
  35. }
  36. // 设置请求中的错误消息属性
  37. WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
  38. return exMv;
  39. }
  40. // <c> 情况二,未生成 ModelAndView 对象,则抛出异常
  41. throw ex;
  42. }

在 Spring MVC 的 DispatcherServlet 处理请求执行方法过程中,不管是否抛出异常都会进行结果处理,如果抛出了异常也需要调用该方法处理异常

可以看到,在 <a> 处会遍历所有的 HandlerExceptionResolver 异常处理器来处理,如果某一个处理器处理成功并返回 ModelAndView 对象,则直接返回

HandlerExceptionResolver 接口

org.springframework.web.servlet.HandlerExceptionResolver,异常处理器接口,代码如下:

  1. public interface HandlerExceptionResolver {
  2. /**
  3. * 解析异常,转换成对应的 ModelAndView 结果
  4. */
  5. @Nullable
  6. ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);
  7. }

HandlerExceptionResolver 接口体系的结构如下:

初始化过程

DispatcherServletinitHandlerExceptionResolvers(ApplicationContext context) 方法,初始化 HandlerExceptionResolver 组件,方法如下:

  1. private void initHandlerExceptionResolvers(ApplicationContext context) {
  2. // 置空 handlerExceptionResolvers 处理
  3. this.handlerExceptionResolvers = null;
  4. // 情况一,自动扫描 HandlerExceptionResolver 类型的 Bean 们
  5. if (this.detectAllHandlerExceptionResolvers) {
  6. // Find all HandlerExceptionResolvers in the ApplicationContext, including ancestor contexts.
  7. Map<String, HandlerExceptionResolver> matchingBeans = BeanFactoryUtils
  8. .beansOfTypeIncludingAncestors(context, HandlerExceptionResolver.class, true, false);
  9. if (!matchingBeans.isEmpty()) {
  10. this.handlerExceptionResolvers = new ArrayList<>(matchingBeans.values());
  11. // We keep HandlerExceptionResolvers in sorted order.
  12. AnnotationAwareOrderComparator.sort(this.handlerExceptionResolvers);
  13. }
  14. }
  15. // 情况二,获得名字为 HANDLER_EXCEPTION_RESOLVER_BEAN_NAME 的 Bean
  16. else {
  17. try {
  18. HandlerExceptionResolver her = context.getBean(HANDLER_EXCEPTION_RESOLVER_BEAN_NAME, HandlerExceptionResolver.class);
  19. this.handlerExceptionResolvers = Collections.singletonList(her);
  20. }
  21. catch (NoSuchBeanDefinitionException ex) {
  22. // Ignore, no HandlerExceptionResolver is fine too.
  23. }
  24. }
  25. // Ensure we have at least some HandlerExceptionResolvers, by registering
  26. // default HandlerExceptionResolvers if no other resolvers are found.
  27. /**
  28. * 情况三,如果未获得到,则获得默认配置的 HandlerExceptionResolver 类
  29. * {@link org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver}
  30. * {@link org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver}
  31. * {@link org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver}
  32. */
  33. if (this.handlerExceptionResolvers == null) {
  34. this.handlerExceptionResolvers = getDefaultStrategies(context, HandlerExceptionResolver.class);
  35. if (logger.isTraceEnabled()) {
  36. logger.trace("No HandlerExceptionResolvers declared in servlet '" + getServletName() +
  37. "': using default strategies from DispatcherServlet.properties");
  38. }
  39. }
  40. }

  1. 如果“开启”探测功能,则扫描已注册的 HandlerExceptionResolver 的 Bean 们,添加到 handlerExceptionResolvers 中,默认开启

  1. 如果“关闭”探测功能,则获得 Bean 名称为 "handlerExceptionResolver" 对应的 Bean ,将其添加至 handlerExceptionResolvers

  1. 如果未获得到,则获得默认配置的 HandlerExceptionResolver 类,调用 getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) 方法,就是从 DispatcherServlet.properties 文件中读取 HandlerExceptionResolver 的默认实现类,如下:

org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver

在 Spring Boot 中,默认配置下会走上述 1 的逻辑,handlerExceptionResolvers 有两个元素:

  • org.springframework.boot.autoconfigure.web.DefaultErrorAttributes:在 Spring Boot 中,逻辑比较简单,暂时忽略

  • org.springframework.web.servlet.handler.HandlerExceptionResolverComposite:复合的 HandlerExceptionResolver 实现类

接下来会对 HandlerExceptionResolverComposite 中的这三种异常处理器进行分析

HandlerExceptionResolverComposite

org.springframework.web.servlet.handler.HandlerExceptionResolverComposite,实现 HandlerExceptionResolver、Ordered 接口,复合的 HandlerExceptionResolver 实现类

构造方法
  1. public class HandlerExceptionResolverComposite implements HandlerExceptionResolver, Ordered {
  2. /**
  3. * 异常解析器数组
  4. */
  5. @Nullable
  6. private List<HandlerExceptionResolver> resolvers;
  7. /**
  8. * 优先级,默认最低
  9. */
  10. private int order = Ordered.LOWEST_PRECEDENCE;
  11. }
  • resolvers:HandlerExceptionResolver 实现类列表

  • order:优先级,默认最低

从上面的初始化过程中可以看到,Spring Boot 默认配置下 HandlerExceptionResolverComposite 包含三个实现类:

  1. org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver

  1. org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver

  1. org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver

resolveException

实现 resolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) 方法,遍历 HandlerExceptionResolver 数组,逐个处理异常 ex,如果成功,则返回 ModelAndView 对象,方法如下:

  1. @Override
  2. @Nullable
  3. public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response,
  4. @Nullable Object handler, Exception ex) {
  5. if (this.resolvers != null) {
  6. for (HandlerExceptionResolver handlerExceptionResolver : this.resolvers) {
  7. ModelAndView mav = handlerExceptionResolver.resolveException(request, response, handler, ex);
  8. if (mav != null) {
  9. return mav;
  10. }
  11. }
  12. }
  13. return null;
  14. }

AbstractHandlerExceptionResolver

org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver,实现 HandlerExceptionResolver、Ordered 接口,HandlerExceptionResolver 抽象类,作为所有 HandlerExceptionResolver 实现类的基类

构造方法
  1. public abstract class AbstractHandlerExceptionResolver implements HandlerExceptionResolver, Ordered {
  2. private static final String HEADER_CACHE_CONTROL = "Cache-Control";
  3. /**
  4. * 优先级,默认最低
  5. */
  6. private int order = Ordered.LOWEST_PRECEDENCE;
  7. /**
  8. * 匹配的处理器对象的集合
  9. */
  10. @Nullable
  11. private Set<?> mappedHandlers;
  12. /**
  13. * 匹配的处理器类型的数组
  14. */
  15. @Nullable
  16. private Class<?>[] mappedHandlerClasses;
  17. /**
  18. * 防止响应缓存
  19. */
  20. private boolean preventResponseCaching = false;
  21. }

上面的这些属性在后续方法中会讲到

shouldApplyTo

shouldApplyTo(HttpServletRequest request, Object handler) 方法,判断当前 HandlerExceptionResolver 是否能应用到传入的 handler 处理器,方法如下:

  1. protected boolean shouldApplyTo(HttpServletRequest request, @Nullable Object handler) {
  2. if (handler != null) {
  3. // <1> 如果 mappedHandlers 包含 handler 对象,则返回 true
  4. if (this.mappedHandlers != null && this.mappedHandlers.contains(handler)) {
  5. return true;
  6. }
  7. // <2> 如果 mappedHandlerClasses 包含 handler 的类型,则返回 true
  8. if (this.mappedHandlerClasses != null) {
  9. for (Class<?> handlerClass : this.mappedHandlerClasses) {
  10. if (handlerClass.isInstance(handler)) {
  11. return true;
  12. }
  13. }
  14. }
  15. }
  16. // Else only apply if there are no explicit handler mappings.
  17. // <3> 如果 mappedHandlers 和 mappedHandlerClasses 都为空,说明直接匹配
  18. return (this.mappedHandlers == null && this.mappedHandlerClasses == null);
  19. }
  1. 如果 mappedHandlers 包含该 handler 处理器对象,则返回 true

  1. 如果 mappedHandlerClasses 包含该 handler 处理器所在类,则返回 true

  1. 如果 mappedHandlersmappedHandlerClasses 都为空,说明直接匹配

prepareResponse

prepareResponse(Exception ex, HttpServletResponse response) 方法,阻止响应缓存,方法如下:

  1. protected void prepareResponse(Exception ex, HttpServletResponse response) {
  2. if (this.preventResponseCaching) {
  3. preventCaching(response);
  4. }
  5. }
  6. /**
  7. * Prevents the response from being cached, through setting corresponding
  8. * HTTP {@code Cache-Control: no-store} header.
  9. * @param response current HTTP response
  10. */
  11. protected void preventCaching(HttpServletResponse response) {
  12. response.addHeader(HEADER_CACHE_CONTROL, "no-store");
  13. }

如果想要阻止响应缓存,需要设置 preventResponseCachingtrue

resolveException

实现 resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) 方法,代码如下

  1. @Override
  2. @Nullable
  3. public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response,
  4. @Nullable Object handler, Exception ex) {
  5. // <1> 判断是否可以应用
  6. if (shouldApplyTo(request, handler)) {
  7. // <1.1> 阻止缓存
  8. prepareResponse(ex, response);
  9. // <1.2> 执行解析异常,返回 ModelAndView 对象
  10. ModelAndView result = doResolveException(request, response, handler, ex);
  11. // <1.3> 如果 ModelAndView 对象非空,则打印日志
  12. if (result != null) {
  13. // Print debug message when warn logger is not enabled.
  14. if (logger.isDebugEnabled() && (this.warnLogger == null || !this.warnLogger.isWarnEnabled())) {
  15. logger.debug("Resolved [" + ex + "]" + (result.isEmpty() ? "" : " to " + result));
  16. }
  17. // Explicitly configured warn logger in logException method.
  18. logException(ex, request);
  19. }
  20. // <1.4> 返回执行结果
  21. return result;
  22. }
  23. // <2> 不可应用,直接返回 null
  24. else {
  25. return null;
  26. }
  27. }
  28. @Nullable
  29. protected abstract ModelAndView doResolveException(
  30. HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);
  1. 调用 shouldApplyTo(HttpServletRequest request, Object handler) 方法,判断是否可以应用,如果可以应用

  1. 调用 prepareResponse(Exception ex, HttpServletResponse response) 方法,阻止缓存

  1. 调用 doResolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) 抽象方法,执行解析异常,返回 ModelAndView 对象

  1. 如果 ModelAndView 对象非空,则打印日志

  1. 返回执行结果

  1. 不可应用,直接返回 null

AbstractHandlerMethodExceptionResolver

org.springframework.web.servlet.handler.AbstractHandlerMethodExceptionResolver,继承 AbstractHandlerExceptionResolver 抽象类,基于 handler 类型为 HandlerMethod 的 HandlerExceptionResolver 抽象类。

可能你会有疑惑,为什么 AbstractHandlerMethodExceptionResolver 只有一个 ExceptionHandlerExceptionResolver 子类,为什么还要做抽象呢?因为 ExceptionHandlerExceptionResolver 是基于 @ExceptionHandler 注解来配置对应的异常处理器,而如果未来我们想自定义其它的方式来配置对应的异常处理器,就可以来继承 (拓展性)AbstractHandlerMethodExceptionResolver 这个抽象类。

有没发现 Spring MVC 中,存在大量的逻辑与配置分离的分层实现
shouldApplyTo

重写 shouldApplyTo(HttpServletRequest request, Object handler) 方法,代码如下:

  1. @Override
  2. protected boolean shouldApplyTo(HttpServletRequest request, @Nullable Object handler) {
  3. // 情况一,如果 handler 为空,则直接调用父方法
  4. if (handler == null) {
  5. return super.shouldApplyTo(request, null);
  6. }
  7. // 情况二,处理 handler 为 HandlerMethod 类型的情况
  8. else if (handler instanceof HandlerMethod) {
  9. // <x> 获得真正的 handler
  10. HandlerMethod handlerMethod = (HandlerMethod) handler;
  11. handler = handlerMethod.getBean();
  12. // 调用父方法
  13. return super.shouldApplyTo(request, handler);
  14. }
  15. // 情况三,直接返回 false
  16. else {
  17. return false;
  18. }
  19. }

重点在于情况二,需要在 <x> 处,调用 HandlerMethod#getBean() 方法,获得真正的 handler 处理器。

doResolveException

重写 doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) 方法,代码如下:

  1. @Override
  2. @Nullable
  3. protected final ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response,
  4. @Nullable Object handler, Exception ex) {
  5. return doResolveHandlerMethodException(request, response, (HandlerMethod) handler, ex);
  6. }
  7. @Nullable
  8. protected abstract ModelAndView doResolveHandlerMethodException(HttpServletRequest request, HttpServletResponse response,
  9. @Nullable HandlerMethod handlerMethod, Exception ex);

handler 转换成 HandlerMethod 类型,并提供新的抽象方法

【重点】ExceptionHandlerExceptionResolver

org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,实现 ApplicationContextAware、InitializingBean 接口,继承 AbstractHandlerMethodExceptionResolver 抽象类,基于 @ExceptionHandler 配置 HandlerMethod 的 HandlerExceptionResolver 实现类。

示例

可能你没有使用 @ExceptionHandler 注解来实现过异常的处理,例如:

  1. @Log4j2
  2. @RestControllerAdvice
  3. public class CustomizeExceptionHandler extends ResponseEntityExceptionHandler {
  4. @ExceptionHandler({EmptyArgumentException.class, IllegalArgumentException.class})
  5. public Result<?> customizeHandleArgumentException(HttpServletRequest request, final Exception e, HttpServletResponse response) {
  6. response.setStatus(HttpStatus.OK.value());
  7. return Result.fail(ResultCode.PARAM_ERROR.getCode(), e.getMessage());
  8. }
  9. @ExceptionHandler({Exception.class})
  10. public Result<?> customizeHandleException(HttpServletRequest request, final Exception e, HttpServletResponse response) {
  11. log.error("异常拦截[{}]:", e.getMessage(), e);
  12. response.setStatus(HttpStatus.OK.value());
  13. return Result.fail(ResultCode.UNKNOWN.getCode(), e.getMessage());
  14. }
  15. }

该自定义异常处理类会处理 Controller 类抛出的指定类型的异常

构造方法
  1. public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExceptionResolver
  2. implements ApplicationContextAware, InitializingBean {
  3. /**
  4. * 自定义的方法参数处理器
  5. */
  6. @Nullable
  7. private List<HandlerMethodArgumentResolver> customArgumentResolvers;
  8. /**
  9. * 方法参数处理器组合
  10. */
  11. @Nullable
  12. private HandlerMethodArgumentResolverComposite argumentResolvers;
  13. /**
  14. * 自定义的执行结果处理器
  15. */
  16. @Nullable
  17. private List<HandlerMethodReturnValueHandler> customReturnValueHandlers;
  18. /**
  19. * 执行结果处理器组合
  20. */
  21. @Nullable
  22. private HandlerMethodReturnValueHandlerComposite returnValueHandlers;
  23. /*
  24. * HTTP 消息转换器
  25. */
  26. private List<HttpMessageConverter<?>> messageConverters;
  27. private ContentNegotiationManager contentNegotiationManager = new ContentNegotiationManager();
  28. /**
  29. * 响应体的后置增强器
  30. */
  31. private final List<Object> responseBodyAdvice = new ArrayList<>();
  32. @Nullable
  33. private ApplicationContext applicationContext;
  34. private final Map<Class<?>, ExceptionHandlerMethodResolver> exceptionHandlerCache = new ConcurrentHashMap<>(64);
  35. private final Map<ControllerAdviceBean, ExceptionHandlerMethodResolver> exceptionHandlerAdviceCache = new LinkedHashMap<>();
  36. public ExceptionHandlerExceptionResolver() {
  37. StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
  38. stringHttpMessageConverter.setWriteAcceptCharset(false); // see SPR-7316
  39. // 初始化 messageConverters
  40. this.messageConverters = new ArrayList<>();
  41. this.messageConverters.add(new ByteArrayHttpMessageConverter());
  42. this.messageConverters.add(stringHttpMessageConverter);
  43. try {
  44. this.messageConverters.add(new SourceHttpMessageConverter<>());
  45. } catch (Error err) {
  46. // Ignore when no TransformerFactory implementation is available
  47. }
  48. this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
  49. }
  50. }

有没有一种熟悉的感觉,和 《HandlerAdapter 组件(一)之 HandlerAdapter》RequestMappingHandlerAdapter 类似,有大量的相同变量,例如参数解析器和返回结果处理器,最终也是调用 ServletInvocableHandlerMethod 的方法。因为你定义也是定义的方法去处理相关的异常

afterPropertiesSet

因为 ExceptionHandlerExceptionResolver 实现了 InitializingBean 接口,在 Sping 初始化该 Bean 的时候,会调用该方法,完成一些初始化工作,方法如下:

  1. @Override
  2. public void afterPropertiesSet() {
  3. // Do this first, it may add ResponseBodyAdvice beans
  4. // 初始化 exceptionHandlerAdviceCache、responseBodyAdvice
  5. initExceptionHandlerAdviceCache();
  6. // 初始化 argumentResolvers 参数
  7. if (this.argumentResolvers == null) {
  8. List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
  9. this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
  10. }
  11. // 初始化 returnValueHandlers 参数
  12. if (this.returnValueHandlers == null) {
  13. List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
  14. this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
  15. }
  16. }
  1. 调用 initExceptionHandlerAdviceCache() 方法,初始化 exceptionHandlerAdviceCacheresponseBodyAdvice,详情见下文

  1. 初始化 argumentResolvers 属性。其中,#getDefaultArgumentResolvers() 方法,获得默认的 HandlerMethodArgumentResolver 数组,详情见下文

  1. 初始化 returnValueHandlers 属性。其中,#getDefaultReturnValueHandlers() 方法,获得默认的 HandlerMethodReturnValueHandler 数组,详情见下文

initExceptionHandlerAdviceCache

initExceptionHandlerAdviceCache() 方法,初始化 exceptionHandlerAdviceCacheresponseBodyAdvice,方法如下:

  1. private void initExceptionHandlerAdviceCache() {
  2. if (getApplicationContext() == null) {
  3. return;
  4. }
  5. // <1> 扫描 @ControllerAdvice 注解的 Bean 们,并将进行排序
  6. List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
  7. AnnotationAwareOrderComparator.sort(adviceBeans);
  8. // <2> 遍历 ControllerAdviceBean 数组
  9. for (ControllerAdviceBean adviceBean : adviceBeans) {
  10. Class<?> beanType = adviceBean.getBeanType();
  11. if (beanType == null) {
  12. throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);
  13. }
  14. // <2.1> 扫描该 ControllerAdviceBean 对应的类型
  15. ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(beanType);
  16. // <2.2> 有 @ExceptionHandler 注解,则添加到 exceptionHandlerAdviceCache 中
  17. if (resolver.hasExceptionMappings()) {
  18. this.exceptionHandlerAdviceCache.put(adviceBean, resolver);
  19. }
  20. // <2.3> 如果该 beanType 类型是 ResponseBodyAdvice 子类,则添加到 responseBodyAdvice 中
  21. if (ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
  22. this.responseBodyAdvice.add(adviceBean);
  23. }
  24. }
  25. if (logger.isDebugEnabled()) {
  26. int handlerSize = this.exceptionHandlerAdviceCache.size();
  27. int adviceSize = this.responseBodyAdvice.size();
  28. if (handlerSize == 0 && adviceSize == 0) {
  29. logger.debug("ControllerAdvice beans: none");
  30. }
  31. else {
  32. logger.debug("ControllerAdvice beans: " +
  33. handlerSize + " @ExceptionHandler, " + adviceSize + " ResponseBodyAdvice");
  34. }
  35. }
  36. }
  1. 调用 ControllerAdviceBeanfindAnnotatedBeans(ApplicationContext context) 方法,扫描 @ControllerAdvice 注解的 Bean 们,并将进行排序,这里就会扫描到上面示例中 CustomizeExceptionHandler 自定义异常处理类

  1. 遍历 ControllerAdviceBean 数组

  1. 创建扫描该 ControllerAdviceBean 对应的类型 ExceptionHandlerMethodResolver 对象 resolver,该对象在下面会分析

  1. @ExceptionHandler 注解,则将resolver添加到 exceptionHandlerAdviceCache

  1. 如果该 beanType 类型是 ResponseBodyAdvice 子类,则添加到 responseBodyAdvice

getDefaultArgumentResolvers

getDefaultArgumentResolvers() 方法,获得默认的 HandlerMethodArgumentResolver 数组,方法如下:

  1. protected List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
  2. List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>();
  3. // Annotation-based argument resolution
  4. resolvers.add(new SessionAttributeMethodArgumentResolver());
  5. resolvers.add(new RequestAttributeMethodArgumentResolver());
  6. // Type-based argument resolution
  7. resolvers.add(new ServletRequestMethodArgumentResolver());
  8. resolvers.add(new ServletResponseMethodArgumentResolver());
  9. resolvers.add(new RedirectAttributesMethodArgumentResolver());
  10. resolvers.add(new ModelMethodProcessor());
  11. // Custom arguments
  12. if (getCustomArgumentResolvers() != null) {
  13. resolvers.addAll(getCustomArgumentResolvers());
  14. }
  15. return resolvers;
  16. }
getDefaultReturnValueHandlers

getDefaultReturnValueHandlers() 方法,获得默认的 HandlerMethodReturnValueHandler 数组,方法如下:

  1. protected List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {
  2. List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>();
  3. // Single-purpose return value types
  4. handlers.add(new ModelAndViewMethodReturnValueHandler());
  5. handlers.add(new ModelMethodProcessor());
  6. handlers.add(new ViewMethodReturnValueHandler());
  7. handlers.add(new HttpEntityMethodProcessor(
  8. getMessageConverters(), this.contentNegotiationManager, this.responseBodyAdvice));
  9. // Annotation-based return value types
  10. handlers.add(new ModelAttributeMethodProcessor(false));
  11. handlers.add(new RequestResponseBodyMethodProcessor(
  12. getMessageConverters(), this.contentNegotiationManager, this.responseBodyAdvice));
  13. // Multi-purpose return value types
  14. handlers.add(new ViewNameMethodReturnValueHandler());
  15. handlers.add(new MapMethodProcessor());
  16. // Custom return value types
  17. if (getCustomReturnValueHandlers() != null) {
  18. handlers.addAll(getCustomReturnValueHandlers());
  19. }
  20. // Catch-all
  21. handlers.add(new ModelAttributeMethodProcessor(true));
  22. return handlers;
  23. }
ExceptionHandlerMethodResolver 类
在 ExceptionHandlerExceptionResolver 的 initExceptionHandlerAdviceCache 方法中会用到,两者的名字太容易混淆了

org.springframework.web.method.annotation.ExceptionHandlerMethodResolver,添加 @ControllerAdvice 注解的 Bean,用于解析添加了 @ExceptionHandler 注解的方法

构造方法
  1. public class ExceptionHandlerMethodResolver {
  2. /**
  3. * A filter for selecting {@code @ExceptionHandler} methods.
  4. *
  5. * MethodFilter 对象,用于过滤带有 @ExceptionHandler 注解的方法
  6. */
  7. public static final MethodFilter EXCEPTION_HANDLER_METHODS = method ->
  8. AnnotatedElementUtils.hasAnnotation(method, ExceptionHandler.class);
  9. /**
  10. * 已经映射的方法
  11. *
  12. * 在 {@link #ExceptionHandlerMethodResolver(Class)} 构造方法中初始化
  13. */
  14. pivate final Map<Class<? extends Throwable>, Method> mappedMethods = new HashMap<>(16);
  15. /**
  16. * 已经匹配的方法
  17. *
  18. * 在 {@link #resolveMethod(Exception)} 方法中初始化
  19. */
  20. private final Map<Class<? extends Throwable>, Method> exceptionLookupCache = new ConcurrentReferenceHashMap<>(16);
  21. public ExceptionHandlerMethodResolver(Class<?> handlerType) {
  22. // <1> 遍历 @ExceptionHandler 注解的方法,这些方法用于处理对应的异常
  23. for (Method method : MethodIntrospector.selectMethods(handlerType, EXCEPTION_HANDLER_METHODS)) {
  24. // <2> 遍历处理的异常集合,获取到该方法能处理哪些异常
  25. for (Class<? extends Throwable> exceptionType : detectExceptionMappings(method)) {
  26. // <3> 添加到 mappedMethods 中
  27. addExceptionMapping(exceptionType, method);
  28. }
  29. }
  30. }
  31. }

mappedMethodsexceptionLookupCache 差别在于,后者是经过查找,比较优先级之后所产生的

  1. 遍历 @ExceptionHandler 注解的方法

  1. 调用 detectExceptionMappings(Method method) 方法,获得方法的异常数组,如下:

  1. private List<Class<? extends Throwable>> detectExceptionMappings(Method method) {
  2. List<Class<? extends Throwable>> result = new ArrayList<>();
  3. // 首先,从方法上的 @ExceptionHandler 注解中,获得要处理的异常类型,添加到 result 中
  4. detectAnnotationExceptionMappings(method, result);
  5. // 其次,如果获取不到,从方法参数中,获得所处理的异常,添加到 result 中
  6. if (result.isEmpty()) {
  7. for (Class<?> paramType : method.getParameterTypes()) {
  8. if (Throwable.class.isAssignableFrom(paramType)) {
  9. result.add((Class<? extends Throwable>) paramType);
  10. }
  11. }
  12. }
  13. // 如果获取不到,则抛出 IllegalStateException 异常
  14. if (result.isEmpty()) {
  15. throw new IllegalStateException("No exception types mapped to " + method);
  16. }
  17. return result;
  18. }
  19. private void detectAnnotationExceptionMappings(Method method, List<Class<? extends Throwable>> result) {
  20. ExceptionHandler ann = AnnotatedElementUtils.findMergedAnnotation(method, ExceptionHandler.class);
  21. Assert.state(ann != null, "No ExceptionHandler annotation");
  22. result.addAll(Arrays.asList(ann.value()));
  23. }

调用 addExceptionMapping(Class<? extends Throwable> exceptionType, Method method) 方法,添加到 mappedMethods 中,如下:

  1. private void addExceptionMapping(Class<? extends Throwable> exceptionType, Method method) {
  2. // 添加到 mappedMethods 中
  3. Method oldMethod = this.mappedMethods.put(exceptionType, method);
  4. // 如果已存在,说明冲突,所以抛出 IllegalStateException 异常
  5. if (oldMethod != null && !oldMethod.equals(method)) {
  6. throw new IllegalStateException("Ambiguous @ExceptionHandler method mapped for [" +
  7. exceptionType + "]: {" + oldMethod + ", " + method + "}");
  8. }
  9. }
hasExceptionMappings

hasExceptionMappings() 方法,判断 mappedMethods 非空,方法如下:

  1. public boolean hasExceptionMappings() {
  2. return !this.mappedMethods.isEmpty();
  3. }
resolveMethod

resolveMethod(Exception exception) 方法,获取解析异常对应的方法,方法如下:

  1. @Nullable
  2. public Method resolveMethod(Exception exception) {
  3. return resolveMethodByThrowable(exception);
  4. }
  5. @Nullable
  6. public Method resolveMethodByThrowable(Throwable exception) {
  7. // 首先,获得异常对应的方法
  8. Method method = resolveMethodByExceptionType(exception.getClass());
  9. // 其次,获取不到,则使用异常 cause 对应的方法
  10. if (method == null) {
  11. Throwable cause = exception.getCause();
  12. if (cause != null) {
  13. method = resolveMethodByExceptionType(cause.getClass());
  14. }
  15. }
  16. return method;
  17. }

按照 exceptionexception.cause 的先后,调用 resolveMethodByExceptionType(Class<? extends Throwable> exceptionType) 方法,获得异常对应的方法,如下:

  1. @Nullable
  2. public Method resolveMethodByExceptionType(Class<? extends Throwable> exceptionType) {
  3. // 首先,先从 exceptionLookupCache 缓存中获得异常对应的处理方法
  4. Method method = this.exceptionLookupCache.get(exceptionType);
  5. // 其次,获取不到,则从 mappedMethods 中获得,并添加到 exceptionLookupCache 中
  6. if (method == null) {
  7. method = getMappedMethod(exceptionType);
  8. this.exceptionLookupCache.put(exceptionType, method);
  9. }
  10. return method;
  11. }

逻辑比较简单,调用 getMappedMethod(Class<? extends Throwable> exceptionType) 方法,获得异常对应的方法,如下:

  1. @Nullable
  2. private Method getMappedMethod(Class<? extends Throwable> exceptionType) {
  3. List<Class<? extends Throwable>> matches = new ArrayList<>();
  4. // 遍历 mappedMethods 数组,匹配异常,添加到 matches 中
  5. for (Class<? extends Throwable> mappedException : this.mappedMethods.keySet()) {
  6. if (mappedException.isAssignableFrom(exceptionType)) {
  7. matches.add(mappedException);
  8. }
  9. }
  10. // 将匹配的结果,排序,选择第一个
  11. if (!matches.isEmpty()) {
  12. matches.sort(new ExceptionDepthComparator(exceptionType));
  13. return this.mappedMethods.get(matches.get(0));
  14. }
  15. else {
  16. return null;
  17. }
  18. }

逻辑比较简单,关于 org.springframework.core.ExceptionDepthComparator 比较器,胖友自己点击 传送门 查看。大体的逻辑是,比较它们和目标类的继承层级,越小越匹配。

getExceptionHandlerMethod

getExceptionHandlerMethod(HandlerMethod handlerMethod, Exception exception) 方法,获得异常对应的 ServletInvocableHandlerMethod 对象,代码如下:

  1. @Nullable
  2. protected ServletInvocableHandlerMethod getExceptionHandlerMethod(
  3. @Nullable HandlerMethod handlerMethod, Exception exception) {
  4. // 处理器的类型
  5. Class<?> handlerType = null;
  6. // <1> 首先,如果 handlerMethod 非空,则先获得 Controller 对应的 @ExceptionHandler 处理器对应的方法
  7. if (handlerMethod != null) {
  8. // Local exception handler methods on the controller class itself.
  9. // To be invoked through the proxy, even in case of an interface-based proxy.
  10. // 获得 handlerType
  11. handlerType = handlerMethod.getBeanType();
  12. // 获得 handlerType 对应的 ExceptionHandlerMethodResolver 对象
  13. ExceptionHandlerMethodResolver resolver = this.exceptionHandlerCache.get(handlerType);
  14. if (resolver == null) {
  15. resolver = new ExceptionHandlerMethodResolver(handlerType);
  16. this.exceptionHandlerCache.put(handlerType, resolver);
  17. }
  18. // 获得异常对应的 Method 处理方法
  19. Method method = resolver.resolveMethod(exception);
  20. // 如果获得该异常对应的 Method 处理方法,则创建 ServletInvocableHandlerMethod 对象,并返回
  21. if (method != null) {
  22. return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method);
  23. }
  24. // For advice applicability check below (involving base packages, assignable types
  25. // and annotation presence), use target class instead of interface-based proxy.
  26. // 获得 handlerType 的原始类。因为,此处有可能是代理对象
  27. if (Proxy.isProxyClass(handlerType)) {
  28. handlerType = AopUtils.getTargetClass(handlerMethod.getBean());
  29. }
  30. }
  31. // <2> 其次,使用 ControllerAdvice 对应的 @ExceptionHandler 处理器对应的方法
  32. for (Map.Entry<ControllerAdviceBean, ExceptionHandlerMethodResolver> entry : this.exceptionHandlerAdviceCache.entrySet()) {
  33. ControllerAdviceBean advice = entry.getKey();
  34. // 如果 ControllerAdvice 支持当前的 handlerType
  35. if (advice.isApplicableToBeanType(handlerType)) {
  36. // 获得 handlerType 对应的 ExceptionHandlerMethodResolver 对象
  37. ExceptionHandlerMethodResolver resolver = entry.getValue();
  38. // 获得异常对应的 Method 处理方法
  39. Method method = resolver.resolveMethod(exception);
  40. if (method != null) {
  41. return new ServletInvocableHandlerMethod(advice.resolveBean(), method);
  42. }
  43. }
  44. }
  45. // 最差,获取不到
  46. return null;
  47. }
  1. 首先,如果 handlerMethod 非空,则先获得 Controller 对应的 @ExceptionHandler 处理器对应的方法,如果获取到了,则将该 Method 封装成 ServletInvocableHandlerMethod 对象并返回

  1. 其次,使用 ControllerAdvice 对应的 @ExceptionHandler 处理器对应的方法,如果获取到了,则将该 Method 封装成 ServletInvocableHandlerMethod 对象并返回

  1. 最差,获取不到,返回 null

上面第 2 种情况也就是示例中定义的方法哦~

doResolveHandlerMethodException

实现 doResolveHandlerMethodException(ttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod, Exception exception) 方法,处理异常,代码如下:

  1. @Override
  2. @Nullable
  3. protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request,
  4. HttpServletResponse response, @Nullable HandlerMethod handlerMethod, Exception exception) {
  5. // <1> 获得异常对应的 ServletInvocableHandlerMethod 对象
  6. ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception);
  7. if (exceptionHandlerMethod == null) {
  8. return null;
  9. }
  10. // <1.1> 设置 ServletInvocableHandlerMethod 对象的相关属性
  11. if (this.argumentResolvers != null) {
  12. exceptionHandlerMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
  13. }
  14. if (this.returnValueHandlers != null) {
  15. exceptionHandlerMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
  16. }
  17. // <1.2> 创建 ServletWebRequest 对象
  18. ServletWebRequest webRequest = new ServletWebRequest(request, response);
  19. // <1.3> 创建 ModelAndViewContainer 对象
  20. ModelAndViewContainer mavContainer = new ModelAndViewContainer();
  21. try {
  22. if (logger.isDebugEnabled()) {
  23. logger.debug("Using @ExceptionHandler " + exceptionHandlerMethod);
  24. }
  25. // <2> // 执行处理该异常的方法 ServletInvocableHandlerMethod 的调用
  26. Throwable cause = exception.getCause();
  27. if (cause != null) {
  28. // Expose cause as provided argument as well
  29. exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, cause, handlerMethod);
  30. }
  31. else {
  32. // Otherwise, just the given exception as-is
  33. exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, handlerMethod);
  34. }
  35. }
  36. catch (Throwable invocationEx) {
  37. // Any other than the original exception is unintended here,
  38. // probably an accident (e.g. failed assertion or the like).
  39. // <2.1> 发生异常,则直接返回
  40. if (invocationEx != exception && logger.isWarnEnabled()) {
  41. logger.warn("Failure in @ExceptionHandler " + exceptionHandlerMethod, invocationEx);
  42. }
  43. // Continue with default processing of the original exception...
  44. return null;
  45. }
  46. // <3> 如果 mavContainer 已处理,则返回 '空的' ModelAndView 对象。
  47. if (mavContainer.isRequestHandled()) {
  48. return new ModelAndView();
  49. }
  50. // <4> 如果 mavContainer 未处,则基于 `mavContainer` 生成 ModelAndView 对象
  51. else {
  52. ModelMap model = mavContainer.getModel();
  53. HttpStatus status = mavContainer.getStatus();
  54. // <4.1> 创建 ModelAndView 对象,并设置相关属性
  55. ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, status);
  56. mav.setViewName(mavContainer.getViewName());
  57. if (!mavContainer.isViewReference()) {
  58. mav.setView((View) mavContainer.getView());
  59. }
  60. // <4.2>
  61. if (model instanceof RedirectAttributes) {
  62. Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
  63. RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
  64. }
  65. return mav;
  66. }
  67. }
  1. 调用 getExceptionHandlerMethod(HandlerMethod handlerMethod, Exception exception) 方法,获得异常对应的 ServletInvocableHandlerMethod 对象

  1. 设置 ServletInvocableHandlerMethod 对象的相关属性,参数解析器,返回结果处理器

  1. 创建 ServletWebRequest 对象 webRequest,封装了请求和响应

  1. 创建 ModelAndViewContainer 对象 mavContainer,用于获取 ModelAndView 对象

  1. 执行处理该异常的方法,ServletInvocableHandlerMethod 对象的调用

  • 此处传入了 Object... providedArgs 参数为 exceptionhandlerMethod 变量,这也是为什么 @ExceptionHanlder 注解的方法,可以设置为这两个参数

  1. 发生异常,则直接返回

  1. 如果 mavContainer 已处理,则返回 “空的” ModelAndView 对象。这样,就不会被后续的 ViewResolver 所处理。为什么呢?可以自己回看下 DispatcherServlet 的 processHandlerException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) 方法,很容易明白

  1. 如果 mavContainer 未处理,则基于 mavContainer 生成 ModelAndView 对象

  1. 创建 ModelAndView 对象,并设置相关属性,视图名称

  1. FlashMapManager 相关,暂时忽略

ResponseStatusExceptionResolver

org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,实现 MessageSourceAware 接口,继承 AbstractHandlerExceptionResolver 抽象类,基于 @ResponseStatus 提供错误响应的 HandlerExceptionResolver 实现类

构造方法
  1. public class ResponseStatusExceptionResolver extends AbstractHandlerExceptionResolver implements MessageSourceAware {
  2. @Nullable
  3. private MessageSource messageSource;
  4. }
applyStatusAndReason

applyStatusAndReason(int statusCode, @Nullable String reason, HttpServletResponse response) 方法,设置错误响应,方法如下:

  1. protected ModelAndView applyStatusAndReason(int statusCode, @Nullable String reason, HttpServletResponse response)
  2. throws IOException {
  3. // 情况一,如果无错误提示,则响应只设置状态码
  4. if (!StringUtils.hasLength(reason)) {
  5. response.sendError(statusCode);
  6. }
  7. // 情况二,如果有错误信息,则响应设置状态码 + 错误提示
  8. else {
  9. // 进一步解析错误提示,如果有 messageSource 的情况下
  10. String resolvedReason = (this.messageSource != null ?
  11. this.messageSource.getMessage(reason, null, reason, LocaleContextHolder.getLocale()) :
  12. reason);
  13. // 设置
  14. response.sendError(statusCode, resolvedReason);
  15. }
  16. // 创建“空” ModelAndView 对象,并返回
  17. return new ModelAndView();
  18. }

注意,此处返回的也是“空”的 ModelAndView 对象。这样,就不会被后续的 ViewResolver 所处理

doResolveException

实现 doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) 方法,代码如下:

  1. @Override
  2. @Nullable
  3. protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response,
  4. @Nullable Object handler, Exception ex) {
  5. try {
  6. // <1> 情况一,如果异常是 ResponseStatusException 类型,进行解析并设置到响应
  7. if (ex instanceof ResponseStatusException) {
  8. return resolveResponseStatusException((ResponseStatusException) ex, request, response, handler);
  9. }
  10. // <2> 情况二,如果有 @ResponseStatus 注解,进行解析并设置到响应
  11. ResponseStatus status = AnnotatedElementUtils.findMergedAnnotation(ex.getClass(), ResponseStatus.class);
  12. if (status != null) {
  13. return resolveResponseStatus(status, request, response, handler, ex);
  14. }
  15. // <3> 情况三,使用异常的 cause 在走一次情况一、情况二的逻辑。
  16. if (ex.getCause() instanceof Exception) {
  17. return doResolveException(request, response, handler, (Exception) ex.getCause());
  18. }
  19. }
  20. catch (Exception resolveEx) {
  21. if (logger.isWarnEnabled()) {
  22. logger.warn("Failure while trying to resolve exception [" + ex.getClass().getName() + "]", resolveEx);
  23. }
  24. }
  25. return null;
  26. }

1、情况一,如果异常是 ResponseStatusException 类型,进行解析并设置到响应,调用 resolveResponseStatusException(ResponseStatusException ex, HttpServletRequest request, HttpServletResponse response, Object handler) 方法,如下:

  1. protected ModelAndView resolveResponseStatusException(ResponseStatusException ex,
  2. HttpServletRequest request, HttpServletResponse response, @Nullable Object handler) throws Exception {
  3. int statusCode = ex.getStatus().value();
  4. String reason = ex.getReason();
  5. return applyStatusAndReason(statusCode, reason, response);
  6. }

2、情况二,如果有 @ResponseStatus 注解,进行解析并设置到响应,调用 resolveResponseStatus(ResponseStatus responseStatus, HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)

方法,如下:

  1. protected ModelAndView resolveResponseStatus(ResponseStatus responseStatus, HttpServletRequest request,
  2. HttpServletResponse response, @Nullable Object handler, Exception ex) throws Exception {
  3. int statusCode = responseStatus.code().value();
  4. String reason = responseStatus.reason();
  5. return applyStatusAndReason(statusCode, reason, response);
  6. }

3、情况三,使用异常的 cause 再走一次情况一情况二的逻辑

DefaultHandlerExceptionResolver

org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver,继承 AbstractHandlerExceptionResolver 抽象类,默认 HandlerExceptionResolver 实现类,针对各种异常,设置错误响应码

其中,实现 doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) 方法,代码如下:

  1. @Override
  2. @Nullable
  3. protected ModelAndView doResolveException(
  4. HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
  5. try {
  6. if (ex instanceof HttpRequestMethodNotSupportedException) {
  7. return handleHttpRequestMethodNotSupported(
  8. (HttpRequestMethodNotSupportedException) ex, request, response, handler);
  9. }
  10. else if (ex instanceof HttpMediaTypeNotSupportedException) {
  11. return handleHttpMediaTypeNotSupported(
  12. (HttpMediaTypeNotSupportedException) ex, request, response, handler);
  13. }
  14. else if (ex instanceof HttpMediaTypeNotAcceptableException) {
  15. return handleHttpMediaTypeNotAcceptable(
  16. (HttpMediaTypeNotAcceptableException) ex, request, response, handler);
  17. }
  18. else if (ex instanceof MissingPathVariableException) {
  19. return handleMissingPathVariable(
  20. (MissingPathVariableException) ex, request, response, handler);
  21. }
  22. else if (ex instanceof MissingServletRequestParameterException) {
  23. return handleMissingServletRequestParameter(
  24. (MissingServletRequestParameterException) ex, request, response, handler);
  25. }
  26. else if (ex instanceof ServletRequestBindingException) {
  27. return handleServletRequestBindingException(
  28. (ServletRequestBindingException) ex, request, response, handler);
  29. }
  30. else if (ex instanceof ConversionNotSupportedException) {
  31. return handleConversionNotSupported(
  32. (ConversionNotSupportedException) ex, request, response, handler);
  33. }
  34. else if (ex instanceof TypeMismatchException) {
  35. return handleTypeMismatch(
  36. (TypeMismatchException) ex, request, response, handler);
  37. }
  38. else if (ex instanceof HttpMessageNotReadableException) {
  39. return handleHttpMessageNotReadable(
  40. (HttpMessageNotReadableException) ex, request, response, handler);
  41. }
  42. else if (ex instanceof HttpMessageNotWritableException) {
  43. return handleHttpMessageNotWritable(
  44. (HttpMessageNotWritableException) ex, request, response, handler);
  45. }
  46. else if (ex instanceof MethodArgumentNotValidException) {
  47. return handleMethodArgumentNotValidException(
  48. (MethodArgumentNotValidException) ex, request, response, handler);
  49. }
  50. else if (ex instanceof MissingServletRequestPartException) {
  51. return handleMissingServletRequestPartException(
  52. (MissingServletRequestPartException) ex, request, response, handler);
  53. }
  54. else if (ex instanceof BindException) {
  55. return handleBindException((BindException) ex, request, response, handler);
  56. }
  57. else if (ex instanceof NoHandlerFoundException) {
  58. return handleNoHandlerFoundException(
  59. (NoHandlerFoundException) ex, request, response, handler);
  60. }
  61. else if (ex instanceof AsyncRequestTimeoutException) {
  62. return handleAsyncRequestTimeoutException(
  63. (AsyncRequestTimeoutException) ex, request, response, handler);
  64. }
  65. }
  66. catch (Exception handlerEx) {
  67. if (logger.isWarnEnabled()) {
  68. logger.warn("Failure while trying to resolve exception [" + ex.getClass().getName() + "]", handlerEx);
  69. }
  70. }
  71. return null;
  72. }

逻辑不复杂,根据不同的异常,设置响应码和错误信息,例如 HTTP 方法类型不支持,如下:

  1. protected ModelAndView handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException ex,
  2. HttpServletRequest request, HttpServletResponse response, @Nullable Object handler) throws IOException {
  3. String[] supportedMethods = ex.getSupportedMethods();
  4. if (supportedMethods != null) {
  5. response.setHeader("Allow", StringUtils.arrayToDelimitedString(supportedMethods, ", "));
  6. }
  7. // 405 状态码,HTTP Method 不支持
  8. response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, ex.getMessage());
  9. return new ModelAndView();
  10. }

注意,返回的都是“空”的 ModelAndView 对象。这样,就不会被后续的 ViewResolver 所处理

总结

本文对 Spring MVC 中的 HandlerExceptionResolver 组件进行分析,处理器异常解析器,将处理器( handler )执行时发生的异常(也就是处理请求,执行方法的过程中发生的异常)解析(转换)成对应的 ModelAndView 结果

HandlerExceptionResolver 的实现类没有特别多,不过也采用了组合模式,如果某个异常处理器进行处理了,也就是返回的 ModeAndView 不为 null(一般都是“空”对象),则直接返回该 ModeAndView 对象

在 Spring MVC 和 Spring Boot 中,默认情况下都有三种 HandlerExceptionResolver 实现类,他们的顺序如下:

  1. ExceptionHandlerExceptionResolver:基于 @ExceptionHandler 配置 HandlerMethod 的 HandlerExceptionResolver 实现类。例如通过 @ControllerAdvice 注解自定义异常处理器,加上@ExceptionHandler注解指定方法所需要处理的异常类型,这种方式就在这个实现类中实现的。没有使用过这两个注解可以参考上面的示例

  1. ResponseStatusExceptionResolver:基于 @ResponseStatus 提供错误响应的 HandlerExceptionResolver 实现类。例如在方法上面添加 @ResponseStatus 注解,指定该方法发生异常时,需要设置的 code 响应码和 reason 错误信息

  1. DefaultHandlerExceptionResolver:默认 HandlerExceptionResolver 实现类,针对各种异常,设置错误响应码。例如 HTTP Method 不支持,则在这个实现类中往响应中设置错误码错误信息

到这里,已经分析了 Spring MVC 的 DispatcherServlet,以及 MultipartResolver、HandlerMapping、HandlerAdapter 和 HandlerExceptionResolver 四个组件,只想说:Spring MVC 的设计者太优秀
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/喵喵爱编程/article/detail/917132
推荐阅读
相关标签
  

闽ICP备14008679号