springboot异常(三):异常处理原理

🍅一、BasicErrorController

☘️1.1 描述

BasicErrorControllerSpringboot中默认的异常处理方法,无需额外的操作,当程序发生了异常之后,Springboot自动捕获异常,重新请求到BasicErrorController中,在BasicErrorController中返回一个视图页面。

🌱1.2 原理解析-配置

ErrorMvcAutoConfiguration会配置Springboot中关于异常相关的类。其中有两个类是异常相关的。

1.2.1 BasicErrorController

配制的第一个BeanBasicErrorController类,所有的异常捕获的时候,都会重新请求到这个Controller

@Bean
@ConditionalOnMissingBean(value = ErrorController.class, search = SearchStrategy.CURRENT)
public BasicErrorController basicErrorController(ErrorAttributes errorAttributes,ObjectProvider<ErrorViewResolver> errorViewResolvers) {return new BasicErrorController(errorAttributes, this.serverProperties.getError(),                            errorViewResolvers.orderedStream().collect(Collectors.toList()));
}

1.2.2 ErrorPageCustomizer

初始化第二个BeanErrorPageCustomizer类,这个类是发生异常之后返回视图的模板页面

// 初始化一个ErrorPageCustomizer类
@Bean
public ErrorPageCustomizer errorPageCustomizer(DispatcherServletPath dispatcherServletPath) {return new ErrorPageCustomizer(this.serverProperties, dispatcherServletPath);
}
// ErrorPageCustomizer类主要的作用
static class ErrorPageCustomizer implements ErrorPageRegistrar, Ordered {private final ServerProperties properties;private final DispatcherServletPath dispatcherServletPath;protected ErrorPageCustomizer(ServerProperties properties, DispatcherServletPath dispatcherServletPath) {this.properties = properties;this.dispatcherServletPath = dispatcherServletPath;}@Overridepublic void registerErrorPages(ErrorPageRegistry errorPageRegistry) {ErrorPage errorPage = new ErrorPage(// 这里获取异常页面的路径,默认是在/error路径下面this.dispatcherServletPath.getRelativePath(this.properties.getError().getPath()));errorPageRegistry.addErrorPages(errorPage);}@Overridepublic int getOrder() {return 0;}
}

🌲1.3 原理解析-触发

当异常发生后会重新请求到BasicErrorController中,该类有两个接口一个是针对json的请求,一个是针对text/html的请求。这里主要看text/html的请求,返回一个ModelAndView。返回什么ModelAndView页面,是通过resolveErrorView方法去解析的。如果没有ModelAndView视图,就自己创建一个默认的返回。

// 处理针对请求是text/html的请求
@RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {HttpStatus status = getStatus(request);Map<String, Object> model = Collections.unmodifiableMap(getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.TEXT_HTML)));response.setStatus(status.value());ModelAndView modelAndView = resolveErrorView(request, response, status, model);return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);
}
// 处理请求是json的请求
@RequestMapping
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {HttpStatus status = getStatus(request);if (status == HttpStatus.NO_CONTENT) {return new ResponseEntity<>(status);}Map<String, Object> body = getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.ALL));return new ResponseEntity<>(body, status);
}
  • resolveErrorView

这里先是获取所有的ErrorViewResolver,然后循环去调用ErrorViewResolverresolveErrorView方法。ErrorViewResolver是一个功能性接口,只有一个方法resolveErrorView

protected ModelAndView resolveErrorView(HttpServletRequest request, HttpServletResponse response, HttpStatus status,Map<String, Object> model) {for (ErrorViewResolver resolver : this.errorViewResolvers) {ModelAndView modelAndView = resolver.resolveErrorView(request, status, model);if (modelAndView != null) {return modelAndView;}}return null;
}
  • DefaultErrorViewResolver

ErrorViewResolver只有一个实现类DefaultErrorViewResolver,在DefaultErrorViewResolverresolverErrorView方法中先调用了resolve方法,这里传的参数是响应吗,比如500404等。这个方法的目的是找到响应吗对应的页面比如500.html404.html等,如果没找到就找5xx.html页面。

@Override
public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {// 这里直接用500来解析是否有这个页面ModelAndView modelAndView = resolve(String.valueOf(status.value()), model);// 如果是空的,就用5xx,去看看有没有这个页面if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {modelAndView = resolve(SERIES_VIEWS.get(status.series()), model);}return modelAndView;
}
  • resolve

resolve方法找到error路径下对应的页面,如果还是不存在就调用resolveResource方法去找。

private ModelAndView resolve(String viewName, Map<String, Object> model) {String errorViewName = "error/" + viewName;TemplateAvailabilityProvider provider = this.templateAvailabilityProviders.getProvider(errorViewName,this.applicationContext);if (provider != null) {return new ModelAndView(errorViewName, model);}return resolveResource(errorViewName, model);
}
  • resolveResource

resolveResource方法先通过getStaticLocations()获取静态文件路径,然后去路径下判断是否有异常页面存在。

getStaticLocations()包含了四个路径:

1.classpath:/METAINF/resources/

2.classpath:/resources

3.classpath:/static/

4.classpath:/public/

private ModelAndView resolveResource(String viewName, Map<String, Object> model) {// 获取静态资源路径for (String location : this.resources.getStaticLocations()) {try {Resource resource = this.applicationContext.getResource(location);resource = resource.createRelative(viewName + ".html");if (resource.exists()) {return new ModelAndView(new HtmlResourceView(resource), model);}}catch (Exception ex) {}}return null;}

🍇二、HandlerExceptionResolver

🌳1.1描述

HandlerExceptionResolver是通过定义一个类实现HandlerExceptionResolver接口,然后重写resolveException方法,这个方法返回一个ModelAndView类。在ModelAndView中可以定义一些异常相关的处理。

@Component
public class MyHandlerExceptionResolver implements HandlerExceptionResolver {@Overridepublic ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {ModelAndView mv = new ModelAndView();mv.setViewName("error");mv.addObject("error", "报错了");mv.addObject("status", "saliwa");return mv;}
}

🌴1.2原理解析-触发

  • doDispatch

要解析HandlerExceptionResolver的原理要从DispatcherServletdoDispatch开始,我们先看一下这个方法的主要流程,省略掉部分无关代码。先去执行对应请求的方法,如果方法里面发生异常捕获异常,无论是否发生异常都会执行processDispatchResult

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {try {ModelAndView mv = null;Exception dispatchException = null;try {// 去执行我们要调用的方法,比如我们请求的某个controller方法,如果在这个controller执行过程中// 发生了异常或者错误都在这里捕获的,并且用dispatchException这个变量来接收返回的异常类mv = ha.handle(processedRequest, response, mappedHandler.getHandler());}catch (Exception ex) {dispatchException = ex;}catch (Throwable err) {dispatchException = new NestedServletException("Handler dispatch failed", err);}// 这个方法无论是否有异常都会执行processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);}catch (Exception ex) {triggerAfterCompletion(processedRequest, response, mappedHandler, ex);}catch (Throwable err) {triggerAfterCompletion(processedRequest, response, mappedHandler,new NestedServletException("Handler processing failed", err));}}
  • processDispatchResult

processDispatchResult方法,这里省略部分无关代码,首先判读异常是否不为空,如果不为空就执行异常处理逻辑,调用processHandlerException方法。

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,@Nullable Exception exception) throws Exception {boolean errorView = false;// 异常不为空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);}}
}
  • processHandlerException

这里主要是去找有没有HandlerExceptionResolver类,如果有,就执行他的resolveException方法,这个方法会返回ModelAndView,如果ModelAndView不为空就返回。

前面我们自定义的类就是实现了HandlerExceptionResolver接口,并且重写了resolveException方法,返回了一个ModelAndView,这里异常就处理结束了。

protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,@Nullable Object handler, Exception ex) throws Exception {ModelAndView exMv = null;if (this.handlerExceptionResolvers != null) {// 这里就是我们自己的实现类,包括两个默认的和我们自己定义的for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {exMv = resolver.resolveException(request, response, handler, ex);if (exMv != null) {break;}}}if (exMv != null) {return exMv;}throw ex;
}

🍈三、ControllerAdvice

🌵3.1 描述

ControllerAdvice是全局异常拦截器,配合ExceptionHandler使用。除了可以拦截Java定义的异常,还可以自定义异常。

先自定义一个异常

@Getter
@Setter
public class MyException extends RuntimeException {private String errorCode;private String errorMessage;public MyException () {super();}public MyException(String errorCode, String errorMessage) {super(errorMessage);this.errorCode = errorCode;this.errorMessage = errorMessage;}
}

自定义一个全局异常拦截器,拦截自定义的异常和空指针异常。

@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {// 自定异常@ExceptionHandler(value = MyException.class)public Object restErrorHandler(HttpServletRequest request, MyException e) {log.error("报错了: ", e);return "错误码:" + e.getErrorCode() + "错误内容:" + e.getErrorMessage();}// 空指针异常@ExceptionHandler(value={java.lang.NullPointerException.class})public String nullPointerExceptionHandler(Exception e){log.error("报错了 ", e);return "错误内容:" + e.getMessage();}
}

🌾3.2 原理解析-配置

3.2.1 WebMvcConfigurationSupport

  • handlerExceptionResolver

WebMvcConfigurationSupport类里面要初始化一个HandlerExceptionResolver类,在初始化这个类之后要,要执行addDefaultHandlerExceptionResolvers方法。

@Bean
public HandlerExceptionResolver handlerExceptionResolver(@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager) {List<HandlerExceptionResolver> exceptionResolvers = new ArrayList<>();configureHandlerExceptionResolvers(exceptionResolvers);if (exceptionResolvers.isEmpty()) {addDefaultHandlerExceptionResolvers(exceptionResolvers, contentNegotiationManager);}extendHandlerExceptionResolvers(exceptionResolvers);HandlerExceptionResolverComposite composite = new HandlerExceptionResolverComposite();composite.setOrder(0);composite.setExceptionResolvers(exceptionResolvers);return composite;
}
  • addDefaultHandlerExceptionResolvers

在这个方法里面先调用了createExceptionHandlerExceptionResolver方法创建了ExceptionHandlerExceptionResolver类。

protected final void addDefaultHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers,ContentNegotiationManager mvcContentNegotiationManager) {ExceptionHandlerExceptionResolver exceptionHandlerResolver = createExceptionHandlerExceptionResolver();exceptionHandlerResolver.setContentNegotiationManager(mvcContentNegotiationManager);exceptionHandlerResolver.setMessageConverters(getMessageConverters());exceptionHandlerResolver.setCustomArgumentResolvers(getArgumentResolvers());exceptionHandlerResolver.setCustomReturnValueHandlers(getReturnValueHandlers());if (jackson2Present) {exceptionHandlerResolver.setResponseBodyAdvice(Collections.singletonList(new JsonViewResponseBodyAdvice()));}if (this.applicationContext != null) {exceptionHandlerResolver.setApplicationContext(this.applicationContext);}exceptionHandlerResolver.afterPropertiesSet();exceptionResolvers.add(exceptionHandlerResolver);ResponseStatusExceptionResolver responseStatusResolver = new ResponseStatusExceptionResolver();responseStatusResolver.setMessageSource(this.applicationContext);exceptionResolvers.add(responseStatusResolver);exceptionResolvers.add(new DefaultHandlerExceptionResolver());}

3.2.2 ExceptionHandlerExceptionResolver

  • afterPropertiesSet

在这个方法里面主要是调用了一个方法initExceptionHandlerAdviceCache

@Overridepublic void afterPropertiesSet() {// Do this first, it may add ResponseBodyAdvice beansinitExceptionHandlerAdviceCache();if (this.argumentResolvers == null) {List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);}if (this.returnValueHandlers == null) {List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);}}
  • initExceptionHandlerAdviceCache

这个方法是最重要的,首先获取有ControllerAdvice这个注解的bean,也就是我们自定义的全局异常拦截器,然后将这个bean转换成ExceptionHandlerMethodResolver

private void initExceptionHandlerAdviceCache() {if (getApplicationContext() == null) {return;}// 获取有ControllerAdvice注解的类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);if (resolver.hasExceptionMappings()) {this.exceptionHandlerAdviceCache.put(adviceBean, resolver);}if (ResponseBodyAdvice.class.isAssignableFrom(beanType)) {this.responseBodyAdvice.add(adviceBean);}}if (logger.isDebugEnabled()) {int handlerSize = this.exceptionHandlerAdviceCache.size();int adviceSize = this.responseBodyAdvice.size();if (handlerSize == 0 && adviceSize == 0) {logger.debug("ControllerAdvice beans: none");}else {logger.debug("ControllerAdvice beans: " +handlerSize + " @ExceptionHandler, " + adviceSize + " ResponseBodyAdvice");}}}

🌿3.3 原理解析-触发

3.3.1 DispatcherServlet

  • doDispatch

要解析HandlerExceptionResolver的原理要从DispatcherServletdoDispatch开始,我们先看一下这个方法的主要流程,省略掉部分无关代码。先去执行对应请求的方法,如果方法里面发生异常捕获异常,无论是否发生异常都会执行processDispatchResult

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {try {ModelAndView mv = null;Exception dispatchException = null;try {// 去执行我们要调用的方法,比如我们请求的某个controller方法,如果在这个controller执行过程中// 发生了异常或者错误都在这里捕获的,并且用dispatchException这个变量来接收返回的异常类mv = ha.handle(processedRequest, response, mappedHandler.getHandler());}catch (Exception ex) {dispatchException = ex;}catch (Throwable err) {dispatchException = new NestedServletException("Handler dispatch failed", err);}// 这个方法无论是否有异常都会执行processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);}catch (Exception ex) {triggerAfterCompletion(processedRequest, response, mappedHandler, ex);}catch (Throwable err) {triggerAfterCompletion(processedRequest, response, mappedHandler,new NestedServletException("Handler processing failed", err));}}
  • processDispatchResult

processDispatchResult方法,这里省略部分无关代码,首先判读异常是否不为空,如果不为空就执行异常处理逻辑,调用processHandlerException方法。

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,@Nullable Exception exception) throws Exception {boolean errorView = false;// 异常不为空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);}}
}
  • processHandlerException

这里主要是去找有没有HandlerExceptionResolver类,如果有,就执行他的resolveException方法,这个方法会返回ModelAndView,如果ModelAndView不为空就返回。

这里我们没有自定义HandlerExceptionResolver,只有DefaultErrorAttributesHandlerExceptionResolverComposite

这里先去调用DefaultErrorAttributes的resolverException方法,这个方法返回的是null,然后会继续调用HandlerExceptionResolverComposite类的resolverException方法。

protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,@Nullable Object handler, Exception ex) throws Exception {ModelAndView exMv = null;if (this.handlerExceptionResolvers != null) {for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {exMv = resolver.resolveException(request, response, handler, ex);if (exMv != null) {break;}}}if (exMv != null) {return exMv;}throw ex;
}

3.3.2 HandlerExceptionResolverComposite

  • resolveException

在这个方法里面会继续找HandlerExceptionResolver,并执行resolve Exception方法。这里的HandlerExceptionResolver有三个ExceptionHandlerExceptionResolverResponseStatusExceptionResolverDefaultHandlerExceptionResolver

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

首先调用的是ExceptionHandlerExceptionResolverresolveException方法,但是这个方法没有resolveException方法,但是它的抽象父类的父类AbstractHandlerExceptionResolver有这个方法

3.3.3AbstractHandlerExceptionResolver

  • resolveException

这里的resolveException方法中,调用了doResolveException去获取ModelAndView

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("Resolved [" + ex + "]" + (result.isEmpty() ? "" : " to " + result));}// Explicitly configured warn logger in logException method.logException(ex, request);}return result;}else {return null;}
}
  • doResolveException

ExceptionHandlerExceptionResolver也没有 doResolveException但是它的父类AbstractHandlerMethodExceptionResolver有这个方法。

3.3.4 AbstractHandlerMethodExceptionResolver

  • doResolveException

这里走的是AbstractHandlerMethodExceptionResolver类的doResolveException方法,这个方法会继续调用doResolveHandlerMethodException,这个方法是在ExceptionHandlerExceptionResolver里面。

@Override
@Nullable
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);
}

3.3.5 ExceptionHandlerExceptionResolver

  • doResolveHandlerMethodException

看下ExceptionHandlerExceptionResolver实现的doResolveHandlerMethodException,这里最重要的是第一行getExceptionHandlerMethod,获取异常处理方法

protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request,HttpServletResponse response, @Nullable HandlerMethod handlerMethod, Exception exception) {ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception);if (exceptionHandlerMethod == null) {return null;}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 wellThrowable 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 ArrayListarguments[arguments.length - 1] = handlerMethod;exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, arguments);}catch (Throwable invocationEx) {// Any other than the original exception (or a cause) is unintended here,// probably an accident (e.g. failed assertion or the like).if (!exceptions.contains(invocationEx) && logger.isWarnEnabled()) {logger.warn("Failure in @ExceptionHandler " + exceptionHandlerMethod, invocationEx);}// Continue with default processing of the original exception...return null;}if (mavContainer.isRequestHandled()) {return new ModelAndView();}else {ModelMap model = mavContainer.getModel();HttpStatus status = mavContainer.getStatus();ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, status);mav.setViewName(mavContainer.getViewName());if (!mavContainer.isViewReference()) {mav.setView((View) mavContainer.getView());}if (model instanceof RedirectAttributes) {Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);}return mav;}}
  • getExceptionHandlerMethod

可以看出这里是先找到有ControllerAdvicebean的类,然后根据异常类型去匹配这个bean里面定义的ExceptionHandler,这里就找到我们自己定义的全局异常处理的ExceptionHandler,进行异常处理。

protected ServletInvocableHandlerMethod getExceptionHandlerMethod(@Nullable HandlerMethod handlerMethod, Exception exception) {Class<?> handlerType = null;if (handlerMethod != null) {// Local exception handler methods on the controller class itself.// To be invoked through the proxy, even in case of an interface-based proxy.handlerType = handlerMethod.getBeanType();ExceptionHandlerMethodResolver resolver = this.exceptionHandlerCache.get(handlerType);if (resolver == null) {resolver = new ExceptionHandlerMethodResolver(handlerType);this.exceptionHandlerCache.put(handlerType, resolver);}Method method = resolver.resolveMethod(exception);if (method != null) {return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method);}// For advice applicability check below (involving base packages, assignable types// and annotation presence), use target class instead of interface-based proxy.if (Proxy.isProxyClass(handlerType)) {handlerType = AopUtils.getTargetClass(handlerMethod.getBean());}}for (Map.Entry<ControllerAdviceBean, ExceptionHandlerMethodResolver> entry : this.exceptionHandlerAdviceCache.entrySet()) {ControllerAdviceBean advice = entry.getKey();if (advice.isApplicableToBeanType(handlerType)) {ExceptionHandlerMethodResolver resolver = entry.getValue();Method method = resolver.resolveMethod(exception);if (method != null) {return new ServletInvocableHandlerMethod(advice.resolveBean(), method);}}}return null;
}

3.3.6 调用时序图

这里的整体调用逻辑如下:

1.首先调用dispatch去执行方法执行目标方法

2.执行完毕之后调用processDispatchResult去处理执行结果

3.如果目标方法抛出了一场就执行processHandlerException去处理异常

4.在processHandlerException中会调用HandlerExceptionResolverCompositeresolveException方法

5.在HandlerExceptionResolverCompositeresolveException方法会继续调用resolveExeption方法

6.这里是调用ExceptionHandlerExceptionResolverresolveException方法

7.但是ExceptionHandlerExceptionResolver没有resolveException方法,但是它父类的父类AbstractHandlerExceptionResolver有这个方法

8.然后在resolveException中又调用了doResolveException方法,ExceptionHandlerExceptionResolver没有这个方法,但是它的父类有

9.在AbstractHandlerMethodExceptionResolverdoResolveException方法中调用了doResolveHandlerMethodException

10.doResolveHandlerMethodExceptionExceptionHandlerExceptionResolver的,里面继续调用了getExceptionHandlerMethod方法

11.整个调用链路就完成了

在这里插入图片描述

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

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

相关文章

网络安全 DVWA通关指南 DVWA Stored Cross Site Scripting (存储型 XSS)

DVWA Stored Cross Site Scripting (存储型 XSS) 文章目录 DVWA Stored Cross Site Scripting (存储型 XSS)XSS跨站原理存储型 LowMediumHighImpossible 参考文献 WEB 安全靶场通关指南 相关阅读 Brute Force (爆破) Command Injection&#xff08;命令注入&#xff09; Cro…

Spring:项目中的统一异常处理和自定义异常

介绍异常的处理方式。在项目中&#xff0c;都会进行自定义异常&#xff0c;并且都是需要配合统一结果返回进行使用。 1.背景引入 &#xff08;1&#xff09;背景介绍 为什么要处理异常&#xff1f;如果不处理项目中的异常信息&#xff0c;前端访问我们后端就是显示访问失败的…

eslint-plugin-react的使用中,所出现的react版本警告

记一次使用eslint-plugin-react的警告 Warning: React version not specified in eslint-plugin-react settings. See https://github.com/jsx-eslint/eslint-plugin-react#configuration . 背景 我们在工程化项目中&#xff0c;常常会通过eslint来约束我们代码的一些统一格…

基于RPA+BERT的文档辅助“悦读”系统 | OPENAIGC开发者大赛高校组AI创作力奖

在第二届拯救者杯OPENAIGC开发者大赛中&#xff0c;涌现出一批技术突出、创意卓越的作品。为了让这些优秀项目被更多人看到&#xff0c;我们特意开设了优秀作品报道专栏&#xff0c;旨在展示其独特之处和开发者的精彩故事。 无论您是技术专家还是爱好者&#xff0c;希望能带给…

关于寻址方式的讨论

### 对话内容 **学生B&#xff08;ESFP&#xff09;**&#xff1a;老师&#xff0c;寻址方式听起来很复杂&#xff0c;能详细讲解一下吗&#xff1f;而且最好能举些具体例子&#xff01;&#x1f60a; **老师&#xff08;ENTP&#xff09;**&#xff1a;当然可以&#xff01;…

JVM(HotSpot):方法区(Method Area)

文章目录 一、内存结构图二、方法区定义三、内存溢出问题四、常量池与运行时常量池 一、内存结构图 1.6 方法区详细结构图 1.8方法区详细结构图 1.8后&#xff0c;方法区是JVM内存的一个逻辑结构&#xff0c;真实内存用的本地物理内存。 且字符串常量池从常量池中移入堆中。 …

蓝队技能-应急响应篇Web内存马查杀Spring框架型中间件型JVM分析Class提取

知识点&#xff1a; 1、应急响应-Web框架内存马-分析&清除 2、应急响应-Web中间件内存马-分析&清除 注&#xff1a;框架型内存马与中间件内存马只要网站重启后就清除了。 目前Java内存马具体分类&#xff1a; 1、传统Web应用型内存马 Servlet型内存马&#xff1a;…

vivado中除法器ip核的使用

看了很多博客&#xff0c;都没写清楚&#xff0c;害 我要实现 reg [9:0] a; 被除数 reg [16:0] b; 除数 wire [39:0] res; 结果 wire [15:0] real_shan; 要实现a/b 则如下这么配置 选择经过几个周期出结果 wire [39:0] res; // dly5 div_gen_0 div_gen_0_inst (.aclk(clk), …

精密制造的革新:光谱共焦传感器与工业视觉相机的融合

在现代精密制造领域&#xff0c;对微小尺寸、高精度产品的检测需求日益迫切。光谱共焦传感器凭借其非接触、高精度测量特性脱颖而出&#xff0c;而工业视觉相机则以其高分辨率、实时成像能力著称。两者的融合&#xff0c;不仅解决了传统检测方式在微米级别测量上的局限&#xf…

通过 LabVIEW 正则表达式读取数值(整数或小数)

在LabVIEW开发中&#xff0c;字符串处理是一个非常常见的需求&#xff0c;尤其是在处理包含复杂格式的数字时。本文通过一个具体的例子来说明如何利用 Match Regular Expression Function 和 Match Pattern Function 读取并解析字符串中的数字&#xff0c;并重点探讨这两个函数…

MyBatis<foreach>标签的用法与实践

foreach标签简介 实践 demo1 简单的一个批量更新&#xff0c;这里传入了一个List类型的集合作为参数&#xff0c;拼接到 in 的后面 &#xff0c;来实现一个简单的批量更新 <update id"updateVislxble" parameterType"java.util.List">update model…

计算机视觉学习路线

计算机视觉&#xff08;Computer Vision&#xff09;是计算机科学的一个重要分支&#xff0c;旨在使计算机能够理解和解释视觉数据。以下是一个详细的计算机视觉学习路线&#xff0c;帮你系统地掌握这个领域所需的知识和技能。 1. 基础数学和编程 在深入学习计算机视觉之前&…

希捷电脑硬盘好恢复数据吗?探讨可能性、方法以及注意事项

在数字化时代&#xff0c;数据已成为我们生活和工作中不可或缺的一部分。希捷电脑硬盘作为数据存储的重要设备&#xff0c;承载着大量的个人文件、工作资料以及珍贵回忆。然而&#xff0c;面对硬盘故障或误操作导致的数据丢失&#xff0c;许多用户不禁要问&#xff1a;希捷电脑…

毕业设计选题:基于ssm+vue+uniapp的鲜花销售小程序

开发语言&#xff1a;Java框架&#xff1a;ssmuniappJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;M…

FLUX.1图像生成模型:AI工程师的实践与探索

文章目录 1 FLUX.1系列模型2 AI工程师的视角3 ComfyUI部署4 FLUX.1部署5 工作流6 面向未来 黑森林实验室&#xff08;Black Forest Labs&#xff09;研发的FLUX.1图像生成模型&#xff0c;以其120亿参数的庞大规模&#xff0c;正在重新定义图像生成技术的新标准。FLUX.1系列模型…

【TabBar嵌套Navigation案例-新特性页面-代码位置 Objective-C语言】

一、接下来,我们来说这个新特性页面 1.首先,看一下我们的示例程序,这里改一下,加一个叹号, command + R, 好,首先啊,这里边有一个新特性页面,当我这个程序是第一次安装、第一次运行、还有呢、就是当这个应用程序更新的时候,我应该去加载这个新特性页面, 然后呢,这…

信息,就是位+上下文什么是文本文件和二进制文件

信息&#xff0c;就是位上下文 计算机系统是由硬件和软件系统组成的&#xff0c;它们共同工作来运行应用程序 hello.c #include <stdio.h>int main(){printf("Hello World~");return 0; }hello程序的生命周期是从一个源程序&#xff08;或者说源文件&#xf…

相机、镜头参数详解以及相关计算公式

一、工业相机参数 1、分辨率 相机每次采集图像的像素点数&#xff0c;也是指这个相机总共有多少个感光晶片。在采集图像时&#xff0c;相机的分辨率对检测精度有很大的影响&#xff0c;在对同样打的视场成像时&#xff0c;分辨率越高&#xff0c;对细节的展示越明显。 相机像素…

RabbitMQ常用管理命令及管理后台

RabbitMQ管理命令 1、用户管理1.1、新增一个用户1.2、查看当前用户列表1.3、设置用户角色1.4、设置用户权限1.5、查看用户权限 2、RabbitMQ的web管理后台2.1、查看rabbitmq 的插件列表2.2、启用插件2.3、禁用插件2.4、访问RabbitMQ的web后台2.4、通过web页面新建虚拟主机 ./rab…

Html jquery下拉select美化插件——selectFilter.js

1. Html jquery下拉select美化插件——selectFilter.js jQuery是一个广泛使用的JavaScript库&#xff0c;它简化了DOM操作、事件处理、动画以及Ajax交互&#xff0c;使得开发者能更高效地构建交互式网页。在本案例中&#xff0c;jquery.selectlist.js插件正是基于jQuery构建的&…