springboot异常(三):异常处理原理
🍅一、BasicErrorController
☘️1.1 描述
BasicErrorController
是Springboot
中默认的异常处理方法,无需额外的操作,当程序发生了异常之后,Springboot
自动捕获异常,重新请求到BasicErrorController
中,在BasicErrorController
中返回一个视图页面。
🌱1.2 原理解析-配置
在ErrorMvcAutoConfiguration
会配置Springboot
中关于异常相关的类。其中有两个类是异常相关的。
1.2.1 BasicErrorController
配制的第一个Bean
是BasicErrorController
类,所有的异常捕获的时候,都会重新请求到这个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
初始化第二个Bean
是ErrorPageCustomizer
类,这个类是发生异常之后返回视图的模板页面
// 初始化一个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;
}
@Override
public void registerErrorPages(ErrorPageRegistry errorPageRegistry) {
ErrorPage errorPage = new ErrorPage(
// 这里获取异常页面的路径,默认是在/error路径下面
this.dispatcherServletPath.getRelativePath(this.properties.getError().getPath()));
errorPageRegistry.addErrorPages(errorPage);
}
@Override
public 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
,然后循环去调用ErrorViewResolver
的resolveErrorView
方法。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
,在DefaultErrorViewResolver
的resolverErrorView
方法中先调用了resolve
方法,这里传的参数是响应吗,比如500
,404
等。这个方法的目的是找到响应吗对应的页面比如500.html
、404.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 {
@Override
public 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
的原理要从DispatcherServlet
的doDispatch
开始,我们先看一下这个方法的主要流程,省略掉部分无关代码。先去执行对应请求的方法,如果方法里面发生异常捕获异常,无论是否发生异常都会执行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
@Override
public void afterPropertiesSet() {
// Do this first, it may add ResponseBodyAdvice beans
initExceptionHandlerAdviceCache();
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
的原理要从DispatcherServlet
的doDispatch
开始,我们先看一下这个方法的主要流程,省略掉部分无关代码。先去执行对应请求的方法,如果方法里面发生异常捕获异常,无论是否发生异常都会执行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
,只有DefaultErrorAttributes
和HandlerExceptionResolverComposite
。
这里先去调用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
有三个ExceptionHandlerExceptionResolver
、ResponseStatusExceptionResolver
、DefaultHandlerExceptionResolver
。
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;
}
首先调用的是ExceptionHandlerExceptionResolver
的resolveException
方法,但是这个方法没有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 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) {
// 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
中会调用HandlerExceptionResolverComposite
的resolveException
方法
5.在HandlerExceptionResolverComposite
的resolveException
方法会继续调用resolveExeption
方法
6.这里是调用ExceptionHandlerExceptionResolver
的resolveException
方法
7.但是ExceptionHandlerExceptionResolver
没有resolveException
方法,但是它父类的父类AbstractHandlerExceptionResolver
有这个方法
8.然后在resolveException
中又调用了doResolveException
方法,ExceptionHandlerExceptionResolver
没有这个方法,但是它的父类有
9.在AbstractHandlerMethodExceptionResolver
的doResolveException
方法中调用了doResolveHandlerMethodException
10.doResolveHandlerMethodException
是ExceptionHandlerExceptionResolver
的,里面继续调用了getExceptionHandlerMethod
方法
11.整个调用链路就完成了