当前位置: 首页 > news >正文

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中

相关文章:

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

问题&#xff1a;异常处理器在SpringMVC中是如何进行初始化以及使用的&#xff1f; Spring MVC提供处理异常的方式主要分为两种&#xff1a; 1、实现HandlerExceptionResolver方式&#xff08;HandlerExceptionResolver是一个接口&#xff0c;在SpringMVC有一些默认的实现也可以…...

系统架构与Tomcat的安装和配置

2023.10.16 今天是学习javaweb的第一天&#xff0c;主要学习了系统架构的相关知识和原理&#xff0c;下载了web服务器软件&#xff1a;Tomcat&#xff0c;并对其进行了配置。 系统架构 包括&#xff1a;C/S架构 和 B/S架构。 C/S架构&#xff1a; Client / Server&#xff0…...

【Shell脚本】根据起止日期获取Alert日志内容

【Shell脚本】根据起止日期获取Alert日志内容 根据输入的起止日期字符串&#xff0c;检索Oracle告警日志&#xff0c;打印中间的日志行内容。 #!/bin/bash # $1 START_TIME_STR, e.g. "Oct 17 07:" # $2 END_TIME_STR, e.g. "Oct 17 08:" source /home/o…...

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. 删掉即可...

【兔子王赠书第2期】《案例学Python(基础篇)》

文章目录 前言推荐图书本书特色本书目录本书样章本书读者对象粉丝福利丨评论免费赠书尾声 前言 随着人工智能和大数据的蓬勃发展&#xff0c;Python将会得到越来越多开发者的喜爱和应用。身边有很多朋友都开始使用Python语言进行开发。正是因为Python是一门如此受欢迎的编程语…...

用户行为数据案例

一、环境要求 HadoopHiveSparkHBase 开发环境。 二、数据描述 本数据集包含了2017-09-11至2017-12-03之间有行为的约5458位随机用户的所有行为&#xff08;行为包括点击、购买、加购、喜欢&#xff09;。数据集的每一行表示一条用户行为&#xff0c;由用户ID、商品ID、商品类…...

selenium教程 —— css定位

说明&#xff1a;本篇博客基于selenium 4.1.0 selenium-css定位 element_css driver.find_element(By.CSS_SELECTOR, css表达式) 复制代码 css定位说明 selenium中的css定位&#xff0c;实际是通过css选择器来定位到具体元素&#xff0c;css选择器来自于css语法 css定位优点…...

Leetcode 1834. Single-Threaded CPU (堆好题)

Single-Threaded CPU Medium You are given n​​​​​​ tasks labeled from 0 to n - 1 represented by a 2D integer array tasks, where tasks[i] [enqueueTimei, processingTimei] means that the i​​​​​​th​​​​ task will be available to process at enque…...

21-数据结构-内部排序-交换排序

简介&#xff1a;主要根据两个数据进行比较从而交换彼此位置&#xff0c;以此类推&#xff0c;交换完全部。主要有冒泡和快速排序两种。 目录 一、冒泡排序 1.1简介&#xff1a; 1.2代码&#xff1a; 二、快速排序 1.1简介&#xff1a; 1.2代码&#xff1a; 一、冒泡排序…...

5-k8s-探针介绍

文章目录 一、探针介绍二、探针类型三、探针定义方式四、探针实例五、启动探针测试六、存活探针测试七、就绪探针测试 一、探针介绍 概念 在 Kubernetes 中 Pod 是最小的计算单元&#xff0c;而一个 Pod 又由多个容器组成&#xff0c;相当于每个容器就是一个应用&#xff0c;应…...

【网络安全 --- MySQL数据库】网络安全MySQL数据库应该掌握的知识,还不收藏开始学习。

四&#xff0c;MySQL 4.1 mysql安装 #centos7默认安装的是MariaDB-5.5.68或者65&#xff0c; #查看版本的指令&#xff1a;[rootweb01 bbs]# rpm -qa| grep mariadb #安装mariadb的最新版&#xff0c;只是更新了软件版本&#xff0c;不会删除之前原有的数据。 #修改yum源的配…...

【MyBatis系列】- 什么是MyBatis

【MyBatis系列】- 什么是MyBatis 文章目录 【MyBatis系列】- 什么是MyBatis一、学习MyBatis知识必备1.1 学习环境准备1.2 学习前掌握知识二、什么是MyBatis三、持久层是什么3.1 为什么需要持久化服务3.2 持久层四、Mybatis的作用五、MyBatis的优点六、参考文档一、学习MyBatis知…...

【Linux】Ubuntu美化bash【教程】

【Linux】Ubuntu美化bash【教程】 文章目录 【Linux】Ubuntu美化bash【教程】1. 查看当前环境中是否有bash2. 安装Synth-Shell3. 配置Synth-Shell4. 取消greeterReference 1. 查看当前环境中是否有bash 查看当前使用的bash echo $SHELL如下所示 sjhsjhR9000X:~$ echo $SHELL…...

微信小程序仿苹果负一屏由弱到强的高斯模糊

进入下面小程序可以体验效果&#xff0c;然后进入更多。查看模糊效果 一、创建小程序组件 二、代码 wxml: <view class"topBar-15"></view> <view class"topBar-14"></view> <view class"topBar-13"></view&…...

js中的new方法

new方法的作用&#xff1a;创建一个实例对象&#xff0c;并继承原对象的属性和方法&#xff1b; new对象内部操作&#xff1a; 1&#xff0c;创建一个新对象&#xff0c;将新对象的proto属性指向原对象的prototype属性&#xff1b; 2&#xff0c;构造函数执行环境中的this指向…...

机器学习-无监督算法之降维

降维&#xff1a;将训练数据中的样本从高维空间转换到低维空间&#xff0c;降维是对原始数据线性变换实现的。为什么要降维&#xff1f;高维计算难&#xff0c;泛化能力差&#xff0c;防止维数灾难优点&#xff1a;减少冗余特征&#xff0c;方便数据可视化&#xff0c;减少内存…...

ubuntu20.04下Kafka安装部署及基础使用

Ubuntu安装kafka基础使用 kafka 安装环境基础安装下载kafka解压文件修改配置文件启动kafka创建主题查看主题发送消息接收消息 工具测试kafka Assistant 工具连接测试基础连接连接成功查看topic查看消息查看分区查看消费组 Idea 工具测试基础信息配置信息当前消费组发送消息消费…...

汉得欧洲x甄知科技 | 携手共拓全球化布局,助力出海中企数智化发展

HAND Europe 荣幸获得华为云颁发的 GrowCloud 合作伙伴奖项&#xff0c;进一步巩固了其在企业数字化领域的重要地位。于 2023 年 10 月 5 日&#xff0c;HAND Europe 参加了华为云荷比卢峰会&#xff0c;并因其在全球拓展方面的杰出贡献而荣获 GrowCloud 合作伙伴奖项的认可。 …...

【Javascript保姆级教程】显示类型转换和隐式类型转换

文章目录 前言一、显式类型转换1.1 字符串转换1.2 数字转换1.3 布尔值转换 二、隐式类型转换2.1 数字与字符串相加2.2 布尔值与数字相乘 总结 前言 JavaScript是一种灵活的动态类型语言&#xff0c;这意味着变量的数据类型可以在运行时自动转换&#xff0c;或者通过显式类型转…...

C++算法前缀和的应用:分割数组的最大值的原理、源码及测试用例

分割数组的最大值 相关知识点 C算法&#xff1a;前缀和、前缀乘积、前缀异或的原理、源码及测试用例&#xff1a;付视频课程 二分 过些天整理基础知识 题目 给定一个非负整数数组 nums 和一个整数 m &#xff0c;你需要将这个数组分成 m 个非空的连续子数组。 设计一个算法…...

挑战杯推荐项目

“人工智能”创意赛 - 智能艺术创作助手&#xff1a;借助大模型技术&#xff0c;开发能根据用户输入的主题、风格等要求&#xff0c;生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用&#xff0c;帮助艺术家和创意爱好者激发创意、提高创作效率。 ​ - 个性化梦境…...

MPNet:旋转机械轻量化故障诊断模型详解python代码复现

目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...

Leetcode 3576. Transform Array to All Equal Elements

Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接&#xff1a;3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到&#xf…...

Day131 | 灵神 | 回溯算法 | 子集型 子集

Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; 笔者写过很多次这道题了&#xff0c;不想写题解了&#xff0c;大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...

oracle与MySQL数据库之间数据同步的技术要点

Oracle与MySQL数据库之间的数据同步是一个涉及多个技术要点的复杂任务。由于Oracle和MySQL的架构差异&#xff0c;它们的数据同步要求既要保持数据的准确性和一致性&#xff0c;又要处理好性能问题。以下是一些主要的技术要点&#xff1a; 数据结构差异 数据类型差异&#xff…...

【JavaSE】绘图与事件入门学习笔记

-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角&#xff0c;以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向&#xff0c;距离坐标原点x个像素;第二个是y坐标&#xff0c;表示当前位置为垂直方向&#xff0c;距离坐标原点y个像素。 坐标体系-像素 …...

C# 求圆面积的程序(Program to find area of a circle)

给定半径r&#xff0c;求圆的面积。圆的面积应精确到小数点后5位。 例子&#xff1a; 输入&#xff1a;r 5 输出&#xff1a;78.53982 解释&#xff1a;由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982&#xff0c;因为我们只保留小数点后 5 位数字。 输…...

Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析

Java求职者面试指南&#xff1a;Spring、Spring Boot、MyBatis框架与计算机基础问题解析 一、第一轮提问&#xff08;基础概念问题&#xff09; 1. 请解释Spring框架的核心容器是什么&#xff1f;它在Spring中起到什么作用&#xff1f; Spring框架的核心容器是IoC容器&#…...

【Nginx】使用 Nginx+Lua 实现基于 IP 的访问频率限制

使用 NginxLua 实现基于 IP 的访问频率限制 在高并发场景下&#xff0c;限制某个 IP 的访问频率是非常重要的&#xff0c;可以有效防止恶意攻击或错误配置导致的服务宕机。以下是一个详细的实现方案&#xff0c;使用 Nginx 和 Lua 脚本结合 Redis 来实现基于 IP 的访问频率限制…...

云安全与网络安全:核心区别与协同作用解析

在数字化转型的浪潮中&#xff0c;云安全与网络安全作为信息安全的两大支柱&#xff0c;常被混淆但本质不同。本文将从概念、责任分工、技术手段、威胁类型等维度深入解析两者的差异&#xff0c;并探讨它们的协同作用。 一、核心区别 定义与范围 网络安全&#xff1a;聚焦于保…...