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

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

  @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,然后循环去调用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 {
    @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的原理要从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

@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的原理要从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 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中会调用HandlerExceptionResolverCompositeresolveException方法

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

6.这里是调用ExceptionHandlerExceptionResolverresolveException方法

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

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

9.在AbstractHandlerMethodExceptionResolverdoResolveException方法中调用了doResolveHandlerMethodException

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

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

在这里插入图片描述


http://www.kler.cn/a/325476.html

相关文章:

  • 什么是 C++ 内联函数?它的作用是什么?
  • 人工智能与SEO优化中的关键词策略解析
  • 从零开始学习 sg200x 多核开发之 eth0 MAC 地址修改
  • ASP.NET 部署到IIS,访问其它服务器的共享文件 密码设定
  • 云速搭助力用友 BIP 平台快速接入阿里云产品
  • Linux从0——1之shell编程4
  • 超详细的华为ICT大赛报名流程
  • golang学习笔记32——哪些是用golang实现的热门框架和工具
  • Android Webview和ScrollView冲突和WebView使用总结
  • 数仓建模:DataX同步Mysql数据到Hive如何批量生成建表语句?| 基于SQL实现
  • cuda程序编译流程
  • Uniapp 跨域
  • 超好用的10款视频剪辑软件,从入门到精通
  • 浅谈GDDRAM的三种寻址模式
  • DigitalOcean 全球负载均衡是什么?
  • DBMS-2.3 数据库设计(3)——数据库规范化设计实现(3NF、BCNF模式分解)
  • 【有啥问啥】具身智能(Embodied AI):人工智能的新前沿
  • 基于Python大数据可视化的民族服饰数据分析系统
  • 智能编辑器、版本控制与自动化脚本
  • Spring Boot入门指南——从零开始构建你的后端服务
  • 自动化check是不是测试?
  • YoloV8改进策略:BackBone改进|PoolFormer赋能YoloV8,视觉检测性能显著提升的创新尝试
  • 《大型 C++项目的代码组织与架构设计秘籍》
  • Mac系统Docker中SQLserver数据库文件恢复记录
  • 【Linux】fork入门级使用
  • 轻量级日志管理系统SpringBoot3+Loki+grafana的使用实例