• SpringMVC源码分析(三)HandlerExceptionResolver启动和异常处理源码分析


    问题:异常处理器在SpringMVC中是如何进行初始化以及使用的?

    Spring MVC提供处理异常的方式主要分为两种:
    1、实现HandlerExceptionResolver方式(HandlerExceptionResolver是一个接口,在SpringMVC有一些默认的实现也可以自定义异常处理器)
    2、@ExceptionHandler注解方式。注解方式也有两种用法:
    (1)使用在Controller内部
    (2)配置@ControllerAdvice一起使用实现全局处理
    下面的HandlerExceptionResolver接口的类的继承关系。

    在这里插入图片描述
    在这里插入图片描述

    补充说明:注解@EnableWebMvc和<mvc:annotation-driven />

    这个注解得作用就是相当于再配置文件中加上 本质都是会默认得注入一些SpringMVC得核心组件,比如RequestMappingHandlerMapping与RequestMappingHandlerAdapter等,而@EnableWebMvc注解 也是一样得作用默认得会加载一些组件。@EnableWebMvc通常是加载配置类上使用的,在初始化父容器的时候会进行注解的解析然后装载默认组件。
    不过SpringMVC中如果配置了 mvc:annotation-driven/ 或者使用了@EnableWebMvc就会引入的 HandlerExceptionResolverComposite,这个是包含ExceptionHandlerExceptionResolver、DefaultHandlerExceptionResolver、ResponseStatusExceptionResolver3个处理器的是一个组合模式,下面会涉及到这个

    一、启动源码分析

    0、DispatcherServlet#initStrategies()

    回归到DispatcherServlet在执行初始化策略中,跳过其他看initHandlerExceptionResolvers(context)

        protected void initStrategies(ApplicationContext context) {
            initMultipartResolver(context);
            initLocaleResolver(context);
            initThemeResolver(context);
            initHandlerMappings(context);
            initHandlerAdapters(context);
            initHandlerExceptionResolvers(context);
            initRequestToViewNameTranslator(context);
            initViewResolvers(context);
            initFlashMapManager(context);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    1 initHandlerExceptionResolvers()

    找到类是HandlerExceptionResolver的Bean,加入到handlerExceptionResolvers,如果没有的话,就用默认的。这里的默认是会取读取DispatcherServlet.properties

    private void initHandlerExceptionResolvers(ApplicationContext context) {
            this.handlerExceptionResolvers = null;
    
            if (this.detectAllHandlerExceptionResolvers) {
                // Find all HandlerExceptionResolvers in the ApplicationContext, including ancestor contexts.
                Map<String, HandlerExceptionResolver> matchingBeans = BeanFactoryUtils
                        .beansOfTypeIncludingAncestors(context, HandlerExceptionResolver.class, true, false);
                if (!matchingBeans.isEmpty()) {
                    this.handlerExceptionResolvers = new ArrayList<>(matchingBeans.values());
                    // We keep HandlerExceptionResolvers in sorted order.
                    AnnotationAwareOrderComparator.sort(this.handlerExceptionResolvers);
                }
            }
            else {
                try {
                    HandlerExceptionResolver her =
                            context.getBean(HANDLER_EXCEPTION_RESOLVER_BEAN_NAME, HandlerExceptionResolver.class);
                    this.handlerExceptionResolvers = Collections.singletonList(her);
                }
                catch (NoSuchBeanDefinitionException ex) {
                    // Ignore, no HandlerExceptionResolver is fine too.
                }
            }
    
            // Ensure we have at least some HandlerExceptionResolvers, by registering
            // default HandlerExceptionResolvers if no other resolvers are found.
            if (this.handlerExceptionResolvers == null) {
                this.handlerExceptionResolvers = getDefaultStrategies(context, HandlerExceptionResolver.class);
                if (logger.isTraceEnabled()) {
                    logger.trace("No HandlerExceptionResolvers declared in servlet '" + getServletName() +
                            "': using default strategies from DispatcherServlet.properties");
                }
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    1.1 配置文件:DispatcherServlet.properties

    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
    
    • 1
    • 2
    • 3

    1.2 getDefaultStrategies()

    如果容器中获取不到 ,就是在使用了@EnableWebMvc和后加载的默认的组件还是获取不到的话,就会从DispatcherServlet.properties中加载默认的异常处理器。

    protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
    		String key = strategyInterface.getName();
    		String value = defaultStrategies.getProperty(key);
    		if (value != null) {
    			String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
    			List<T> strategies = new ArrayList<>(classNames.length);
    			for (String className : classNames) {
    				try {
    					Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
    					Object strategy = createDefaultStrategy(context, clazz);
    					strategies.add((T) strategy);
    				}
    				catch (ClassNotFoundException ex) {
    					throw new BeanInitializationException(
    							"Could not find DispatcherServlet's default strategy class [" + className +
    							"] for interface [" + key + "]", ex);
    				}
    				catch (LinkageError err) {
    					throw new BeanInitializationException(
    							"Unresolvable class definition for DispatcherServlet's default strategy class [" +
    							className + "] for interface [" + key + "]", err);
    				}
    			}
    			return strategies;
    		}
    		else {
    			return new LinkedList<>();
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    1.2.1 createDefaultStrategy()

    SpringMVC中的几个默认的异常处理器就是经过这个方法,但是这个方式创建出来的bean对象是不归Spring管理的。

    protected Object createDefaultStrategy(ApplicationContext context, Class<?> clazz) {
    		return context.getAutowireCapableBeanFactory().createBean(clazz);
    	}
    
    • 1
    • 2
    • 3

    0、常见的默认异常处理器

    1 SimpleMappingExceptionResolver

    基本不用刻意忽略

    2 ResponseStatusExceptionResolver

    用于处理通过@ResponseStatus注解处理的异常

    // 实现了接口MessageSourceAware,方便拿到国际化资源,方便错误消息的国际化
    // @since 3.0
    public class ResponseStatusExceptionResolver extends AbstractHandlerExceptionResolver implements MessageSourceAware {
    
    	@Nullable
    	private MessageSource messageSource;
    	@Override
    	public void setMessageSource(MessageSource messageSource) {
    		this.messageSource = messageSource;
    	}
    
    
    	@Override
    	@Nullable
    	protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
    		try {
    			// 若异常类型是,那就处理这个异常
    			// 处理很简单:response.sendError(statusCode, resolvedReason)
    			// 当然会有国际化消息的处理。最终new一个空的new ModelAndView()供以返回
    			if (ex instanceof ResponseStatusException) {
    				return resolveResponseStatusException((ResponseStatusException) ex, request, response, handler);
    			}
    
    			// 若异常类型所在的类上标注了ResponseStatus注解,就处理这个状态码
    			//(可见:异常类型优先于ResponseStatus)
    			// 处理方式同上~~~~
    			ResponseStatus status = AnnotatedElementUtils.findMergedAnnotation(ex.getClass(), ResponseStatus.class);
    			if (status != null) {
    				return resolveResponseStatus(status, request, response, handler, ex);
    			}
    
    			// 这里有个递归:如果异常类型是Course里面的,也会继续处理,所以需要注意这里的递归处理
    			if (ex.getCause() instanceof Exception) {
    				return doResolveException(request, response, handler, (Exception) ex.getCause());
    			}
    		} catch (Exception resolveEx) { // 处理失败,就记录warn日志(非info哦~)
    			if (logger.isWarnEnabled()) {
    				logger.warn("Failure while trying to resolve exception [" + ex.getClass().getName() + "]", resolveEx);
    			}
    		}
    		return null;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    3 DefaultHandlerExceptionResolver

    用于处理Spring MVC自己抛出的一些特定的异常

    异常类型状态码
    MissingPathVariableException500
    ConversionNotSupportedException500
    HttpMessageNotWritableException500
    AsyncRequestTimeoutException503
    MissingServletRequestParameterException400
    ServletRequestBindingException400
    TypeMismatchException400
    HttpMessageNotReadableException400
    MethodArgumentNotValidException400
    MissingServletRequestPartException400
    BindException400
    NoHandlerFoundException404
    HttpRequestMethodNotSupportedException405
    HttpMediaTypeNotAcceptableException406
    HttpMediaTypeNotSupportedException415
    // @since 3.0
    public class DefaultHandlerExceptionResolver extends AbstractHandlerExceptionResolver {
    	public DefaultHandlerExceptionResolver() {
    		setOrder(Ordered.LOWEST_PRECEDENCE);
    		setWarnLogCategory(getClass().getName()); // 不同的日志采用不同的记录器是个很好的习惯
    	}
    
    	@Override
    	@Nullable
    	protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
    		try {
    			if (ex instanceof HttpRequestMethodNotSupportedException) {
    				return handleHttpRequestMethodNotSupported(
    						(HttpRequestMethodNotSupportedException) ex, request, response, handler);
    			} else if (ex instanceof HttpMediaTypeNotSupportedException) {
    				return handleHttpMediaTypeNotSupported(
    						(HttpMediaTypeNotSupportedException) ex, request, response, handler);
    			} ... // 省略其它的else if
    			// 多有的handle方法几乎一样的,都是response.sendError()
    			// 有的还会esponse.setHeader("Accept", MediaType.toString(mediaTypes));等等
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    4 ExceptionHandlerExceptionResolver

    用于处理通过@ExceptionHandler注解处理的方法抛出的异常,这个异常处理器是用的最多的。

    public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExceptionResolver
    		implements ApplicationContextAware, InitializingBean {
    		。。。。。。。。。。
    }
    
    • 1
    • 2
    • 3
    • 4

    4.0 ExceptionHandlerExceptionResolver

    继承自AbstractHandlerMethodExceptionResolver,该类主要处理Controller中用@ExceptionHandler注解定义的方法。
    该类也是配置中定义的HandlerExceptionResolver实现类之一,大多数异常处理都是由该类操作。
    SpringMVC中如果配置了 mvc:annotation-driven/ 或者就会引入的 HandlerExceptionResolverComposite,
    在这里插入图片描述
    1、argumentResolvers和customArgumentResolvers是参数解析器,负责将HTTP请求数据解析成异常处理方法的形参对象。

    2、returnValueHandlers和customReturnValueHandlers是返回值处理器,负责将异常处理方法的返回值进行处理。

    3、messageConverters是数据转换的底层工具,一方面从HTTP请求中读取并转换数据成对象,另一方面将对象转成HTTP响应数据。

    4、contentNegotiationManager是负责媒体内容的校验,即根据Content-Tepe进行不同处理。

    5、responseBodyAdvice是ResponseBodyAdvice的缓存,即支持在异常处理返回值写到输出流前的切面处理。

    6、applicationContext是Spring上下文,可以从中获取容器中内容。

    7、exceptionHandlerCache是异常-异常处理方法的缓存。

    8、exceptionHandlerAdviceCache是@ControllerAdvice全局异常处理器的缓存。

    4.1 afterPropertiesSet()

    在创建这个ExceptionHandlerExceptionResolver对象的时候会执行他的afterPropertiesSet(),在这方法中会去获取@ControllerAdvice定义的全局ExceptionHandler方法

    	@Override
    	public void afterPropertiesSet() {
    		// Do this first, it may add ResponseBodyAdvice beans
    		//初始化 @ControllerAdvice 的 bean
    		initExceptionHandlerAdviceCache();
    
    		if (this.argumentResolvers == null) {
    			//获取 默认的参数解析器 DefaultArgumentResolvers
    			List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
    			this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
    		}
    		if (this.returnValueHandlers == null) {
    			//获取 默认的返回值处理器 DefaultReturnValueHandlers
    			List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
    			this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    4.1.1 initExceptionHandlerAdviceCache()

    在nitExceptionHandlerAdviceCache()方法中,会从Spring容器中获取所有@ControllerAdvice标注的bean。遍历这些bean,将其中@ExceptionHandler标注的异常处理方法缓存到exceptionHandlerAdviceCache。如果这些bean实现了ResponseBodyAdvice接口,还会缓存到responseBodyAdvice:

    private void initExceptionHandlerAdviceCache() {  
       // 获取所有@ControllerAdvice标注的bean
       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);  
          // 添加exceptionHandlerAdviceCache缓存
          if (resolver.hasExceptionMappings()) {  
             this.exceptionHandlerAdviceCache.put(adviceBean, resolver);  
          }  
          // 添加responseBodyAdvice缓存
          if (ResponseBodyAdvice.class.isAssignableFrom(beanType)) {  
             this.responseBodyAdvice.add(adviceBean);  
          }  
       }   
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    4.2 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());
    		}
    
    		// Catch-all
    		resolvers.add(new PrincipalMethodArgumentResolver());
    
    		return resolvers;
    	}
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    4.3 getDefaultReturnValueHandlers()

    获取返回值处理器,自此对象创建完成。

    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;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    二、异常处理流程源码分析

    1、DispatcherServlet#processDispatchResult()

    在DispatcherServlet#doDispatch()处理请求过程中抛出异常,会在DispatcherServlet#processDispatchResult()方法中进行异常处理, 如果监测到了非ModelAndViewDefiningException异常,会调用DispatcherServlet#processHandlerException()方法进行异常处理:

    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {  
       try {    
          try {  
             // 文件请求处理
             processedRequest = checkMultipart(request);  
             // 请求地址映射
             mappedHandler = getHandler(processedRequest);  
             // 获取处理器适配器
             HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());  
             // 拦截器预处理
             if (!mappedHandler.applyPreHandle(processedRequest, response)) {  
                return;  
             }  
             // 实际处理请求
             mv = ha.handle(processedRequest, response, mappedHandler.getHandler());  
             // 拦截器后处理
             mappedHandler.applyPostHandle(processedRequest, response, mv);  
          }  
          catch (Exception ex) {  
             dispatchException = ex;  
          }  
          catch (Throwable err) {  
             // As of 4.3, we're processing Errors thrown from handler methods as well,  
             // making them available for @ExceptionHandler methods and other scenarios.         dispatchException = new NestedServletException("Handler dispatch failed", err);  
          }  
          // 异常处理
          processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);  
       }  
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,  
          @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,  
          @Nullable Exception exception) throws Exception {  
       if (exception != null) {  
          if (exception instanceof ModelAndViewDefiningException) {  
             logger.debug("ModelAndViewDefiningException encountered", exception);  
             mv = ((ModelAndViewDefiningException) exception).getModelAndView();  
          }  
          else {  
             Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);  
             mv = processHandlerException(request, response, handler, exception);  
             errorView = (mv != null);  
          }  
       }  
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    2、processHandlerException()

    由于默认初始化添加的是HandlerExceptionResolverComposite处理器,所以执行的也是当前类的resolveException();

    protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,  
          @Nullable Object handler, Exception ex) throws Exception {  
       // Check registered HandlerExceptionResolvers...  
       ModelAndView exMv = null;  
       if (this.handlerExceptionResolvers != null) {  
          for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {  
             exMv = resolver.resolveException(request, response, handler, ex);  
             if (exMv != null) {  
                break;  
             }  
          }  
       }  
       // ……
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    2.1 HandlerExceptionResolverComposite#resolveException()

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

    先会调用其父类AbstractHandlerExceptionResolver的resolveException()方法

    public ModelAndView resolveException(  
          HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {  
      
       if (shouldApplyTo(request, handler)) {  
          prepareResponse(ex, response);  
          ModelAndView result = doResolveException(request, response, handler, ex);  
          if (result != null) {  
             // Print debug message when warn logger is not enabled.  
             if (logger.isDebugEnabled() && (this.warnLogger == null || !this.warnLogger.isWarnEnabled())) {  
                logger.debug(buildLogMessage(ex, request) + (result.isEmpty() ? "" : " to " + result));  
             }  
             // Explicitly configured warn logger in logException method.  
             logException(ex, request);  
          }  
          return result;  
       }  
       else {  
          return null;  
       }  
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    2.1.1.1 AbstractHandlerMethodExceptionResolver#doResolveException()

    进行类型转换

    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);  
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    2.1.1.1.1ExceptionHandlerExceptionResolver#doResolveHandlerMethodException()

    从缓存中获取异常对应的处理方法,添加argumentResolvers和returnValueHandlers。解析异常作为请求参数,最后调用异常处理方法进行异常处理。

    protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request,  
          HttpServletResponse response, @Nullable HandlerMethod handlerMethod, Exception exception) {    
       // 从缓存中获取异常对应的处理方法
       ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception);  
       if (exceptionHandlerMethod == null) {  
          return null;  
       }  
      
       // 添加argumentResolvers和returnValueHandlers
       if (this.argumentResolvers != null) {  
          exceptionHandlerMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);  
       }  
       if (this.returnValueHandlers != null) {  
          exceptionHandlerMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);  
       }  
      
       ServletWebRequest webRequest = new ServletWebRequest(request, response);  
       ModelAndViewContainer mavContainer = new ModelAndViewContainer();  
      
       // 递归添加抛出的异常,作为请求参数
       ArrayList<Throwable> exceptions = new ArrayList<>();  
       try {  
          if (logger.isDebugEnabled()) {  
             logger.debug("Using @ExceptionHandler " + exceptionHandlerMethod);  
          }  
          // Expose causes as provided arguments as well  
          Throwable exToExpose = exception;  
          while (exToExpose != null) {  
             exceptions.add(exToExpose);  
             Throwable cause = exToExpose.getCause();  
             exToExpose = (cause != exToExpose ? cause : null);  
          }  
          Object[] arguments = new Object[exceptions.size() + 1];  
          exceptions.toArray(arguments);  // efficient arraycopy call in ArrayList  
          arguments[arguments.length - 1] = handlerMethod;  
          // 调用异常处理方法进行异常处理
          exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, arguments);  
       }  
       catch (Throwable invocationEx) {  
          return null;  
       }  
       if (mavContainer.isRequestHandled()) {  
          return new ModelAndView();  
       }  
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    其中ExceptionHandlerExceptionResolver#getExceptionHandlerMethod方法,根据request请求的方法和抛出的异常可以匹配到对应的handleMethod。getExceptionHandlerMethod这个方法可以支持单独使用@ExceptionHandler,既使用了@ExceptionHandler又使用了@ControllerAdvice两种方式。
    在ExceptionHandlerExceptionResolver#initExceptionHandlerAdviceCache方法中,ExceptionHandlerExceptionResolver已经扫描了所有@ControllerAdvice注解的Bean,并将其封装成了一个又一个的ExceptionHandlerMethodResolver,而构造ExceptionHandlerMethodResolver时,ExceptionHandlerMethodResolver就会扫描这个Bean下所有的@ExceptionHandler注解的方法。
    所以可以先匹配这个controller是否有使用@ExceptionHandler,如果没有,则从缓存的ControllerAdviceBean中匹配异常。

    2.1.1.1.1.1 ServletInvocableHandlerMethod#getExceptionHandlerMethod()
    protected ServletInvocableHandlerMethod getExceptionHandlerMethod(HandlerMethod handlerMethod, Exception exception) {
        //得到controller
        Class<?> handlerType = (handlerMethod != null ? handlerMethod.getBeanType() : null);
    
        if (handlerMethod != null) {
            //先尝试从缓存中获取ExceptionHandlerMethodResolver
            //ExceptionHandlerMethodResolver有缓存所有`@ExceptionHandler`注解的方法
            ExceptionHandlerMethodResolver resolver = this.exceptionHandlerCache.get(handlerType);
            if (resolver == null) {
                //没获取到的话,构造一个并将其放入缓存中
                resolver = new ExceptionHandlerMethodResolver(handlerType);
                this.exceptionHandlerCache.put(handlerType, resolver);
            }
            //根据异常获取对应的handler_method
            //如果不为空,则说明这个controoler配置了`@ExceptionHandler`
            Method method = resolver.resolveMethod(exception);
            if (method != null) {
                return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method);
            }
        }
    
        //如果controller没有配置`@ExceptionHandler`,则使用统一配置的`@ControllerAdvice`
        //遍历所有的`@ControllerAdvice`,根据异常匹配对应的handler_method
        for (Entry<ControllerAdviceBean, ExceptionHandlerMethodResolver> entry : this.exceptionHandlerAdviceCache.entrySet()) {
            if (entry.getKey().isApplicableToBeanType(handlerType)) {
                ExceptionHandlerMethodResolver resolver = entry.getValue();
                Method method = resolver.resolveMethod(exception);
                //如果匹配倒了,返回这个method
                if (method != null) {
                    return new ServletInvocableHandlerMethod(entry.getKey().resolveBean(), method);
                }
            }
        }
    
        return null;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    2.1.1.1.1.2 ServletInvocableHandlerMethod#invokeAndHandle()

    和之前的HandleMapping一眼的处理流程

    public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
            Object... providedArgs) throws Exception {
    
        //调用反射handler_method,拿到handler_method执行的结果`returnValue`
        Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
        //如果这个handler_method上,还标注了`@ResponseStatus`注解
        //设置response的http状态码和错误原因
        setResponseStatus(webRequest);
    
        //将执行结果保存到ModelAndViewContainer中
        if (returnValue == null) {
            if (isRequestNotModified(webRequest) || hasResponseStatus() || mavContainer.isRequestHandled()) {
                mavContainer.setRequestHandled(true);
                return;
            }
        }
        else if (StringUtils.hasText(this.responseReason)) {
            mavContainer.setRequestHandled(true);
            return;
        }
    
        mavContainer.setRequestHandled(false);
        try {
            //处理执行的返回值
            this.returnValueHandlers.handleReturnValue(
                    returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
        }
        catch (Exception ex) {
            if (logger.isTraceEnabled()) {
                logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex);
            }
            throw ex;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    3、总结流程

    1、ExceptionHandlerExceptionResolver根据请求的方法和抛出的异常,匹配对应的异常处理方法
    2、先匹配controller中有的@ExceptionHandler标注了的方法
    3、再匹配@ControllerAdvice中的@ExceptionHandler标注的方法
    4、执行异常处理方法,获取返回值
    5、返回值输出到response中

  • 相关阅读:
    力扣-279题 完全平方数(C++)- 完全背包
    03、GO语言变量定义、函数
    解决serviceaccount用户认证挂载密文token文件失败导致pod使用anonymous用户问题
    (AHB VIP为例)Verdi UVM/interactive debug mode简单使用
    Linux小知识---关于socket的一些知识点
    npm安装依赖过慢
    GoLang之go常用的并发模型
    MyBatis之xml配置的解析
    基于C#的校园闲置物品共享系统的开发和实现(Asp.net+Web)
    70. 爬楼梯 --力扣 --JAVA
  • 原文地址:https://blog.csdn.net/springsdl/article/details/133846558