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.mzph.cn/news/108509.shtml

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

相关文章

【算法练习Day22】 组合总和 III电话号码的字母组合

​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;练题 &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 文章目录 组合总和 III剪枝 电话号码…

node 通过axios发送post请求(FormData)

方案一&#xff1a; const axios require(axios) const FormData require(form-data) const fs require(fs)const sdUpscaleOnAzure async (req, res) > {const data new FormData()data.append(image, fs.readFileSync(/temp/ai/sd/download/1.png))let config {hea…

R/d2及S/C4估计总体标准差,比较其CPK及规格限概率的差异

R/d2 和 S/C4 是用于估计总体标准差的无偏估计方法&#xff0c;通常用于控制图中。这些估计方法的主要目的是通过样本数据来估计总体标准差&#xff0c;以便监测过程的稳定性和变异性&#xff0c;而不需要收集整个总体的数据。 具体来说&#xff1a; R图中的 R/d2 和 S图中的…

gitlab自编译 源码下载

网上都是怎么用 gitlab&#xff0c;但是实际开发中有需要针对 gitlab 进行二次编译自定义实现功能的想法。 搜索了网上的资料以及在官网的查找&#xff0c;查到了如下 gitlab 使用 ruby 开发。 gitlab 下载包 gitlab/gitlab-ce - Packages packages.gitlab.com gitlab/gitl…

本地搭建渲染农场和云渲染农场哪个更推荐?看完帮你省下几个w

&#xfeff; 渲染农场是由众多机器组成的渲染集群&#xff0c;主要用于渲染单帧效果图或动画项目。凭借渲染农场的强大计算能力&#xff0c;设计师能够满足3D项目紧迫的交期要求。最近&#xff0c;小编注意到许多设计师对以下问题产生了疑惑&#xff1a; 是否可以自行搭建渲…

MySQL查询优化看一篇就够了

关联查询优化 数据准备 #分类 CREATE TABLE IF NOT EXISTS type( id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, card INT(10) UNSIGNED NOT NULL, PRIMARY KEY ( id ) );#图书 CREATE TABLE IF NOT EXISTS book(bookid INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,cardINT(10…

fatal:Could not read from remote repository解决方法

Linux服务器如何连接GitHub&#xff1f; 生成SSH密钥 ssh-keygen -C “邮箱” -t rsa 存放位置一般是/root/.ssh/id_rsa 登录个人github&#xff0c;添加客户端生成的公钥 打开Settings&#xff0c;点击SSH and GPG keys&#xff0c;点击New SSH Key。Key中粘贴id_rsa.pub…

以太网UDP数据回环实验

一、TCP/IP协议簇 前面说到TCP/IP是一个协议簇&#xff0c;其中包含有IP协议、TCP协议、UDP协议、ARP协议、DNS协议、FTP协议等。设备之间要想完成通信&#xff0c;就必须通过这些网络通信协议。 物理层的主要作用就是传输比特流&#xff08;将1、0转化为电流强弱来进行传输&am…

【来点小剧场--项目测试报告】个人博客项目自动化测试

前述 针对个人博客项目进行测试&#xff0c;个人博客主要由七个页面构成&#xff1a;注册页、登录页、个人博客列表页、博客发布页、博客修改页、博客列表页、博客详情页&#xff0c;主要功能包括&#xff1a;注册、登录、编辑并发布博客、修改已发布的博客、查看详情、删除博…

力扣环形链表(1)进阶环形链表(2)及环形链表的约瑟夫问题

为了加深对环形链表的理解和掌握&#xff0c;这两道题是很不错的选择。 这里所说环形链表不是一个圈圈的结构&#xff0c;而是带环链表。 链接&#xff1a;环形链表&#xff08;1&#xff09; 注意这里链表的长度 所以要注意链表是否为空 第一种方法&#xff0c;应该是比较容易…

使用Premiere、PhotoShop和Audition做视频特效

今天接到一个做视频的任务&#xff0c;给一个精忠报国的视频&#xff0c;要求&#xff1a;   ①去掉人声&#xff0c;就是将唱歌的人声去掉&#xff0c;只留下伴奏&#xff1b;   ②截图视频中的横幅&#xff0c;做一个展开的效果&#xff0c;类似卷纸慢慢展开&#xff1b;…

【LeetCode刷题(数据结构)】:二叉树的前序遍历

给你二叉树的根节点root 返回它节点值的前序遍历 示例1&#xff1a; 输入&#xff1a;root [1,null,2,3] 输出&#xff1a;[1,2,3] 示例 2&#xff1a; 输入&#xff1a;root [] 输出&#xff1a;[] 示例 3&#xff1a; 输入&#xff1a;root [1] 输出&#xff1a;[1] 示例…

【iOS】计算器仿写

文章目录 前言一、构建View界面二、Model中进行数据处理三、Controller层实现View与Model交互总结 前言 在前两周组内进行了计算器的仿写&#xff0c;计算器仿写主要用到了MVC框架的思想以及数据结构中用栈进行四则运算的思想&#xff0c;还有就是对OC中的字符串进行各种判错操…

【Java基础面试十二】、说一说你对面向对象的理解

文章底部有个人公众号&#xff1a;热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享&#xff1f; 踩过的坑没必要让别人在再踩&#xff0c;自己复盘也能加深记忆。利己利人、所谓双赢。 面试官&#xff1a; 说一说你对面向对象的理…

C++前缀和算法:构造乘积矩阵

基础知识点 C算法&#xff1a;前缀和基础 题目 给你一个下标从 0 开始、大小为 n * m 的二维整数矩阵 grid &#xff0c;定义一个下标从 0 开始、大小为 n * m 的的二维矩阵 p。如果满足以下条件&#xff0c;则称 p 为 grid 的 乘积矩阵 &#xff1a; 对于每个元素 p[i][j] …

【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] …

【Java基础面试十三】、面向对象的三大特征是什么?

文章底部有个人公众号&#xff1a;热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享&#xff1f; 踩过的坑没必要让别人在再踩&#xff0c;自己复盘也能加深记忆。利己利人、所谓双赢。 面试官&#xff1a;面向对象的三大特征是什…

MySQL单表查询基础综合练习

一、单表查询 素材&#xff1a; 表名&#xff1a;worker-- 表中字段均为中文&#xff0c;比如 部门号 工资 职工号 参加工作 等 CREATE TABLE worker ( 部门号 int(11) NOT NULL, 职工号 int(11) NOT NULL, 工作时间 date NOT NULL, 工资 float(8,2) NOT NULL, 政治面貌 v…

Linux文件管理(上)

一、VIM编辑器 1、vi概述 vi&#xff08;visual editor&#xff09;编辑器通常被简称为vi&#xff0c;它是Linux和Unix系统上最基本的文本编辑器&#xff0c;类似于Windows 系统下的notepad&#xff08;记事本&#xff09;编辑器。 2、vim编辑器 Vim(Vi improved)是vi编辑器…

联邦学习+梯度+梯度剪枝

联邦学习需要参与者在每一次的本地训练后&#xff0c;上传所更新的模型参数并与其他参与者共享&#xff0c;而参数更新中仍有可能包含所有者的敏感信息 解决方案&#xff1a; 加密方法&#xff08;安全多方计算、同态加密&#xff09;通过将明文编码为密文的方式&#xff0c;…