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

news2024/11/19 2:36:06

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

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

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

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

这个注解得作用就是相当于再配置文件中加上<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 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.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 getDefaultStrategies()

如果容器中获取不到 ,就是在使用了@EnableWebMvc和<mvc:annotation-driven />后加载的默认的组件还是获取不到的话,就会从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.1 createDefaultStrategy()

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

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

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

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

4 ExceptionHandlerExceptionResolver

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

public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExceptionResolver
		implements ApplicationContextAware, InitializingBean {
		。。。。。。。。。。
}

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

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

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

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

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;  
         }  
      }  
   }  
   // ……
}

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

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

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

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

其中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;
}

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

3、总结流程

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1099386.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

(ubuntu) 安装JDK

文章目录 前言参看java版本的命令&#xff1a;安装jdk命令安装jps关闭防火墙&#xff1a;查看端口占用&#xff1a;&#xff08;坑&#xff09;ubuntu上Mysql默认标明 区分大小写 前言 提示&#xff1a;常以为人是一个容器&#xff0c;盛着快乐&#xff0c;盛着悲哀。但是人不…

JUC并发编程——线程池学习:基础概念及三大方法、七大参数、四大拒绝策略(基于狂神说的学习笔记)

线程池 池化技术的本质&#xff1a;事先准备好一些资源&#xff0c;线程复用&#xff0c;用完即还&#xff0c;方便管理 默认大小&#xff1a;2 最大并发数max 根据电脑去设置&#xff0c;CPU密集型&#xff0c;IO密集型 线程池的好处&#xff1a; 降低资源的消耗提高响应的…

【马蹄集】—— 概率论专题

概率论专题 目录 MT2226 抽奖概率MT2227 饿饿&#xff01;饭饭&#xff01;MT2228 甜甜花的研究MT2229 赌石MT2230 square MT2226 抽奖概率 难度&#xff1a;黄金    时间限制&#xff1a;1秒    占用内存&#xff1a;128M 题目描述 小码哥正在进行抽奖&#xff0c;箱子里有…

双目项目实战---测距(获取三维坐标和深度信息)

目录 1.简介 2.模块讲解 2.1 立体校正 2.1.1 校正目的 2.1.2 校正方法 2.2 立体匹配和视差计算 2.3 深度计算 3.完整代码 1.简介 双目视觉是一种通过两个摄像机&#xff08;或者两个镜头&#xff09;同时拍摄到同一个场景&#xff0c;再通过计算机算法来获取该场景深度…

C++ - 智能指针的定制删除器

前言 在上一篇C 文章当中&#xff0c;对 智能指针的使用&#xff0c;做了很详细的介绍&#xff0c;对 C11 和 C98 库当中实现的一些常用智能指针做了很详细的介绍&#xff0c;但是智能指针的使用还有一些拓展用法。上篇文章链接&#xff1a;C - 智能指针 - auto_ptr - unique_…

Stm32_标准库_16_串口蓝牙模块_手机与蓝牙模块通信_手机传入信息能对芯片时间日期进行更改

实现了手机发送信息给蓝牙模块&#xff0c;程序对数据进行分析拆解&#xff0c;并更新自身数据 main.c: #include "stm32f10x.h" // Device header #include "Delay.h" #include "OLED.h" #include "Serial.h" #include "Ti…

无人驾驶路径规划(一)全局路径规划 - RRT算法原理及实现

前言&#xff1a;由于后续可能要做一些无人驾驶相关的项目和实验&#xff0c;所以这段时间学习一些路径规划算法并自己编写了matlab程序进行仿真。开启这个系列是对自己学习内容的一个总结&#xff0c;也希望能够和优秀的前辈们多学习经验。 一、无人驾驶路径规划 众所周知&a…

Google Authenticator 和gitlab使用的方法配置Google AuthenticatorGoogle

Google Authenticator 和gitlab使用的方法 目录概述需求&#xff1a; 设计思路实现思路分析1.配置过程&#xff1a; 参考资料和推荐阅读 Survive by day and develop by night. talk for import biz , show your perfect code,full busy&#xff0c;skip hardness,make a bette…

D201126 D201129 支持以太网高级物理层(APL)

D201126 D201129 支持以太网高级物理层(APL) 全球技术和软件领导者艾默生宣布了基于其无限自动化愿景&#xff0c;并作为其下一代以软件为中心的工业自动化架构的基础。新技术的发布将超越传统的控制系统&#xff0c;创建一个更先进的自动化平台&#xff0c;为人类和塑造世界…

【网络】网络层协议:IP(待更新)

文章目录 IP 协议1. 基本概念2. IP 报头解析 &#x1f53a;IP 的 网段划分&#xff1a; IP 协议 IP 不是面向字节流的&#xff0c;而是发送接收一个个的数据包 1. 基本概念 主机&#xff1a;配有 IP 地址的设备 路由器&#xff1a;配有单个或多个 IP 地址&#xff0c;且能进行…

【1314. 矩阵区域和】

目录 一、题目描述二、算法思想三、代码实现 一、题目描述 二、算法思想 三、代码实现 class Solution { public:vector<vector<int>> matrixBlockSum(vector<vector<int>>& mat, int k) {//先预处理数组int nmat.size();//行int mmat[0].size();…

flutter开发实战-下拉刷新与上拉加载更多实现

flutter开发实战-下拉刷新与上拉加载更多实现 在开发中经常遇到列表需要下拉刷新与上拉加载更多&#xff0c;这里使用EasyRefresh&#xff0c;版本是3.3.21 一、什么是EasyRefresh EasyRefresh可以在Flutter应用程序上轻松实现下拉刷新和上拉加载。它几乎支持所有Flutter Sc…

C++指针和引用

1、引用必须初始化&#xff0c;指针不必&#xff0c;所以说引用使你更安全的指针&#xff1b; 2、在汇编代码&#xff0c;指针和引用一模一样&#xff1b; 3、引用只有一级引用&#xff0c;没有多级引用&#xff1b; 4、引用必须引用一个能取地址的变量&#xff1b; 左值&…

第三章 内存管理 五、动态分区分配算法(首次适应算法、最佳适应算法、最坏适应算法、临近适应算法)

目录 一、首次适应算法 1、算法思想&#xff1a; 2、如何实现&#xff1a; 3、两种常用的数据结构: &#xff08;1&#xff09;空闲分区表、空闲分区链 4、例子 二、最佳适应算法 1、算法思想: 2、如何实现: 3、例子&#xff1a; 三、最坏适应算法 1、算法思想&…

蓝桥杯每日一题2023.10.16

数的分解 - 蓝桥云课 (lanqiao.cn) 题目描述 题目分析 最开始想使用dfs&#xff0c;发现范围过大无法在规定时间运行 #include<bits/stdc.h> using namespace std; const int N 2e5 10; int a[N], v[N], ans; void dfs(int dep, int sum, int start) {if(sum > 20…

Linux命令行下查看实时网速

Linux命令行下&#xff0c;用ifconfig可以看到每个网卡实时的收发的数据包了字节数&#xff0c;但并不方便查看当前网卡的实时网速。 近日偶得一个软件叫nload可以方便的在命令行下查看实时网速&#xff0c;不敢独享&#xff0c;分享给大家。 1、安装 sudo apt install nload…

第三章 内存管理 六、基本分页存储管理

目录 一、定义 二、例子 三、总结 一、定义 基本分页存储管理是一种操作系统的存储管理技术。在基本分页存储管理中&#xff0c;物理内存被划分成固定大小的块&#xff0c;称为页面&#xff08;Page&#xff09;&#xff0c;而程序代码和数据被分成相同大小的块&#xff0c;…

数据结构:二叉树(1)

目录 树的概念 树的表示形式 二叉树 二叉树的性质 题目 二叉树的存储 链式存储 初始化二叉树 二叉树的遍历 前序遍历&#xff1a;根&#x1f449;左子树&#x1f449;右子树 中序遍历&#xff1a;左子树&#x1f449;根&#x1f449;右子树 后序遍历&#xff1a;左子…

二十八、【滤镜】

文章目录 滤镜库Camera Raw滤镜神经网络滤镜(Neurel Filters)液化其他滤镜 滤镜库 可以在滤镜库中选择一些常用的滤镜方式&#xff0c;主要有六大类&#xff1a; Camera Raw滤镜 Camera Raw滤镜是photoshop最重要的一个滤镜&#xff0c;他帮助我们在摄影后期去进行颜色预处…

导数、偏导数、方向导数

一、导数 导数是描述函数变化率的数学概念。 导数的定义式: 那么就有一个问题&#xff0c;为什么求函数的最大值点是要对其自变量求导&#xff0c;并使其导数等于0 比如:求L(θ)3lnθ2ln(1-θ)的最大值点 既然导数表示函数的变化率&#xff0c;那么当函数L(θ)的导数等于0&…