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

问题:异常处理器在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 {@Nullableprivate MessageSource messageSource;@Overridepublic void setMessageSource(MessageSource messageSource) {this.messageSource = messageSource;}@Override@Nullableprotected 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@Nullableprotected 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 AbstractHandlerMethodExceptionResolverimplements 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方法

	@Overridepublic void afterPropertiesSet() {// Do this first, it may add ResponseBodyAdvice beans//初始化 @ControllerAdvice 的 beaninitExceptionHandlerAdviceCache();if (this.argumentResolvers == null) {//获取 默认的参数解析器 DefaultArgumentResolversList<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);}if (this.returnValueHandlers == null) {//获取 默认的返回值处理器 DefaultReturnValueHandlersList<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标注的beanList<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 resolutionresolvers.add(new SessionAttributeMethodArgumentResolver());resolvers.add(new RequestAttributeMethodArgumentResolver());// Type-based argument resolutionresolvers.add(new ServletRequestMethodArgumentResolver());resolvers.add(new ServletResponseMethodArgumentResolver());resolvers.add(new RedirectAttributesMethodArgumentResolver());resolvers.add(new ModelMethodProcessor());// Custom arguments//合并了自定义的参数解析器if (getCustomArgumentResolvers() != null) {resolvers.addAll(getCustomArgumentResolvers());}// Catch-allresolvers.add(new PrincipalMethodArgumentResolver());return resolvers;}

4.3 getDefaultReturnValueHandlers()

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

protected List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>();// Single-purpose return value typeshandlers.add(new ModelAndViewMethodReturnValueHandler());handlers.add(new ModelMethodProcessor());handlers.add(new ViewMethodReturnValueHandler());handlers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.contentNegotiationManager, this.responseBodyAdvice));// Annotation-based return value typeshandlers.add(new ServletModelAttributeMethodProcessor(false));handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.contentNegotiationManager, this.responseBodyAdvice));// Multi-purpose return value typeshandlers.add(new ViewNameMethodReturnValueHandler());handlers.add(new MapMethodProcessor());// Custom return value types//合并了自定义的返回值处理器if (getCustomReturnValueHandlers() != null) {handlers.addAll(getCustomReturnValueHandlers());}// Catch-allhandlers.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和returnValueHandlersif (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) {//得到controllerClass<?> 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_methodfor (Entry<ControllerAdviceBean, ExceptionHandlerMethodResolver> entry : this.exceptionHandlerAdviceCache.entrySet()) {if (entry.getKey().isApplicableToBeanType(handlerType)) {ExceptionHandlerMethodResolver resolver = entry.getValue();Method method = resolver.resolveMethod(exception);//如果匹配倒了,返回这个methodif (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.hqwc.cn/news/138575.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

STM32Cube高效开发教程<基础篇>(六)----FSMC连接TFT-LCD屏

声明:本人水平有限,博客可能存在部分错误的地方,请广大读者谅解并向本人反馈错误。    本专栏博客参考《STM32Cube高效开发教程(基础篇)》,有意向的读者可以购买正版书籍辅助学习,本书籍由王维波老师、鄢志丹老师、王钊老师倾力打造,书籍内容干货满满。 一、 FSMC连接…

“第四十二天”

这个&#xff0c;之前用的b去存储a的总和和排名&#xff0c;后来在比较的过程中&#xff0c;只改变的b的值&#xff0c;却没有改变a的值&#xff0c;但在比较语文成绩的时候用的还是a&#xff0c;这个时候a和b同样是第i个对应的可能不是同一个对象了 &#xff0c;因为上面b的值…

rabbitMq (2)

RabbitMQ 消息应答与发布 文章目录 1. 消息应答1.2 自动应答1.2 手动应答1.3 代码案例 2. RabbitMQ 持久化2.1 队列持久化2.2 消息持久化 3. 不公平分发4. 预取值分发5. 发布确认5.1 发布确认逻辑5.2 开启发布确认的方法5.3 单个确认发布5.4 批量确认发布5.5 异步确认5.5.1 处理…

《动手学深度学习 Pytorch版》 8.7 通过时间反向传播

8.7.1 循环神经网络的梯度分析 本节主要探讨梯度相关问题&#xff0c;因此对模型及其表达式进行了简化&#xff0c;进行如下表示&#xff1a; h t f ( x t , h t − 1 , w h ) o t g ( h t , w o ) \begin{align} h_t&f(x_t,h_{t-1},w_h)\\ o_t&g(h_t,w_o) \end{ali…

在 VSCode 中使用 PlantUML

最近&#xff0c;因为工作需要绘制一些逻辑图&#xff0c;我自己现在使用的是 PlantUML 或者 mermaid&#xff0c;相比之下前者更加强大。不过它的环境也麻烦一些&#xff0c;mermaid 在一些软件上已经内置了。但是 PlantUML 一般需要自己本地安装或者使用远程服务器&#xff0…

git学习——第4节 时光机穿梭

我们已经成功地添加并提交了一个readme.txt文件&#xff0c;现在&#xff0c;是时候继续工作了&#xff0c;于是&#xff0c;我们继续修改readme.txt文件&#xff0c;改成如下内容&#xff1a; Git is a distributed version control system. Git is free software. 现在&…

Library projects cannot set applicationId. applicationId is set to

Library projects cannot set applicationId. applicationId is set to com.xxx.library_cache in default config. 删掉即可

主流压力测试工具推荐

在产品研发过程中&#xff0c;常常会混淆压力/负载/性能测试这三者之间的区别&#xff0c;这三种测试到底有什么不同呢&#xff1f; 压力测试&#xff08;StressTesting&#xff09;&#xff0c;也称为强度测试&#xff0c;通过模拟实际应用的软硬件环境及用户使用过程的系统负…

【LeetCode热题100】--75.颜色分类

75.颜色分类 方法一&#xff1a;使用单指针 class Solution {public void sortColors(int[] nums) {int n nums.length;int ptr 0;for(int i 0;i<n;i){if(nums[i] 0){int tmp nums[i];nums[i] nums[ptr];nums[ptr] tmp;ptr;}}for(int i ptr;i<n;i){if(nums[i] …

大数据学习(12)-join优化common join

&&大数据学习&& &#x1f525;系列专栏&#xff1a; &#x1f451;哲学语录: 承认自己的无知&#xff0c;乃是开启智慧的大门 &#x1f496;如果觉得博主的文章还不错的话&#xff0c;请点赞&#x1f44d;收藏⭐️留言&#x1f4dd;支持一下博>主哦&#x…

VScode platformio的使用

一、platformio 工程创建 打开vscode界面你会发现左下多了个家的小图标&#xff0c;点击这里就可以进入platformio。 在右侧Quick Access栏中&#xff0c;有4个选项。可以看得出来&#xff0c;我们这里直接点击创建一个新的工程。 点击New Project打开project配置界面&#x…

Kotlin中的数值类型

在Kotlin中&#xff0c;Byte、Short、Int、Long、Float和Double是基本数据类型&#xff0c;用于表示不同范围和精度的数值。 Byte&#xff08;字节&#xff09;&#xff1a;Byte类型是8位有符号整数类型&#xff0c;取值范围为-128到127。在Kotlin中&#xff0c;可以使用字面值…