mvc 异常处理源码解析(3)

准备

  1. 准备一个controller类, 里面抛出一个异常
@RestController
@RequestMapping("/hello")
public class HelloController {


    @GetMapping("/h")
    public String hello(HttpServletResponse response) throws ArithmeticException {

        try {
            int a = 1 / 0;
        } catch (Exception e) {
            throw e;
        }
        return "hello";
    }

}

  1. 配置一个全局异常处理类
/**
 * 异常处理器
 */
@RestControllerAdvice
public class BDExceptionHandler {

    /**
     * 参数异常
     * @param e 异常
     * @return BaseResult
     */
    @ExceptionHandler(ArithmeticException.class)
    public R doBaseExceptionHandler(ArithmeticException e) {
        return R.error(e.getMessage());
    }
}
  1. 接下来访问url, 跟踪源码

源码跟踪

进入到mvc的入口类DispatcherServlet的 doDispatch方法, 可以发现整体逻辑被try catch包围, 当第3步抛出异常之后就会被捕获, dispatchException对象则不为空,最后进入processDispatchResult方法进行处理

	protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HttpServletRequest processedRequest = request;
		HandlerExecutionChain mappedHandler = null;
		boolean multipartRequestParsed = false;

		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

		try {
			ModelAndView mv = null;
			Exception dispatchException = null;

			try {
				//1.获取处理器链路
				//2.执行拦截器前置方法
				//3.调用controller方法, 返回结果集
				//4.执行拦截器后置方法
			}
			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));
		}
		finally {
			if (asyncManager.isConcurrentHandlingStarted()) {
				// Instead of postHandle and afterCompletion
				if (mappedHandler != null) {
					mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
				}
			}
			else {
				// Clean up any resources used by a multipart request.
				if (multipartRequestParsed) {
					cleanupMultipart(processedRequest);
				}
			}
		}
	}

进入processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

	private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
			@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
			@Nullable Exception exception) throws Exception {

		boolean errorView = false;
		//判断异常是否为空, 如果不是空则处理
		if (exception != null) {
			//处理ModelAndViewDefiningException异常
			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);
			}
		}
		//省略下面代码

进入mv = processHandlerException(request, response, handler, exception);

	protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
			@Nullable Object handler, Exception ex) throws Exception {

		// Success and error responses may use different content types
		request.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);

		// Check registered HandlerExceptionResolvers...
		ModelAndView exMv = null;
		if (this.handlerExceptionResolvers != null) {
			//this.handlerExceptionResolvers里面默认有两个处理异常的类
			for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
				// 遍历,判断使用那个类来处理
				exMv = resolver.resolveException(request, response, handler, ex);
				if (exMv != null) {
					break;
				}
			}
		}
		if (exMv != null) {
			//省略代码
			return exMv;
		}

		throw ex;
	}

看一下上面for循环里面的this.handlerExceptionResolvers对象的结构 :
DefaultErrorAttributes : 没做任何处理, 只是将异常设置到request对象的属性当中;

HandlerExceptionResolverComposite : 里面有三个异常处理类, 分别的作用是 :

ExceptionHandlerExceptionResolver:基于 @ExceptionHandler 配置 HandlerMethod 的 HandlerExceptionResolver 实现类。例如通过 @ControllerAdvice 注解自定义异常处理器,加上@ExceptionHandler注解指定方法所需要处理的异常类型

ResponseStatusExceptionResolver:基于 @ResponseStatus 提供错误响应的 HandlerExceptionResolver 实现类。例如在方法上面添加 @ResponseStatus 注解,指定该方法发生异常时,需要设置的 code 响应码和 reason 错误信息

DefaultHandlerExceptionResolver:默认 HandlerExceptionResolver 实现类,针对各种异常,设置错误响应码。例如 HTTP Method 不支持,则在这个实现类中往响应中设置错误码和错误信息

原文链接:https://blog.csdn.net/Running666/article/details/130786561

在这里插入图片描述
进入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;
	}

由于只设置了第一种异常处理方式, 所以异常会由ExceptionHandlerExceptionResolver来处理, 进入handlerExceptionResolver.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("Resolved [" + ex + "]" + (result.isEmpty() ? "" : " to " + result));
				}
				
				logException(ex, request);
			}
			return result;
		}
		else {
			return null;
		}
	}

进入doResolveException(request, response, handler, ex)方法;

	@Override
	@Nullable
	protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request,
			HttpServletResponse response, @Nullable HandlerMethod handlerMethod, Exception exception) {
			
		//根据controller类和异常来判断由哪个类来处理这个异常
		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 {
			//省略代码
			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;
		}
		//省略
	}

主要逻辑就是看如何找到处理对应异常的方法, 进入getExceptionHandlerMethod(handlerMethod, exception);

进入到了ExceptionHandlerExceptionResolver类中, 在这个类里面有两个关键的属性,exceptionHandlerCache和exceptionHandlerAdviceCache, 两个都是map, exceptionHandlerAdviceCache的初始化下面再说,exceptionHandlerCache的初始化想知道的自己去查一下吧

	private final Map<Class<?>, ExceptionHandlerMethodResolver> exceptionHandlerCache =
			new ConcurrentHashMap<>(64);

	private final Map<ControllerAdviceBean, ExceptionHandlerMethodResolver> exceptionHandlerAdviceCache =
			new LinkedHashMap<>();

exceptionHandlerCache里面存的就是在当前controller里面包含@ExceptionHandler注解的方法, key: controller类, value : 方法包装类

exceptionHandlerAdviceCache里面存的就是标注了@ControllerAdvice注解的类,里面带有@ExceptionHandler注解的方法, key:
@RestControllerAdvice注解标注的类, value: 方法包装类

	@Nullable
	protected ServletInvocableHandlerMethod getExceptionHandlerMethod(
			@Nullable HandlerMethod handlerMethod, Exception exception) {

		Class<?> handlerType = null;
		//从exceptionHandlerCache获取异常对应的处理方法, 由于没有配置, 肯定获取不到
		if (handlerMethod != null) {
			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);
			}
			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;
	}

进入Method method = resolver.resolveMethod(exception);, 最终进入到resolveMethodByExceptionType()方法, 在ExceptionHandlerMethodResolver类中

ExceptionHandlerMethodResolver中有两个关键属性mappedMethods,和exceptionLookupCache, 两个也都是map, 两个都是根据异常获取对应的异常处理方法, mappedMethods在程序启动时候初始化, exceptionLookupCache相当于一个缓存,为了提高查询速度

	@Nullable
	public Method resolveMethodByExceptionType(Class<? extends Throwable> exceptionType) {
		//从缓存中获取处理方法, 如果获取不到,返回null(第一次执行此方法肯定获取不到)
		Method method = this.exceptionLookupCache.get(exceptionType);
		if (method == null) {
			//如果获取不到, 则去mappedMethods获取
			method = getMappedMethod(exceptionType);
			//将获取的结果缓存到exceptionLookupCache中
			this.exceptionLookupCache.put(exceptionType, method);
		}
		return (method != NO_MATCHING_EXCEPTION_HANDLER_METHOD ? method : null);
	}

进入getMappedMethod(exceptionType);

	private Method getMappedMethod(Class<? extends Throwable> exceptionType) {
		List<Class<? extends Throwable>> matches = new ArrayList<>();
		//遍历所有可以处理的异常, 判断与抛出的异常是否匹配
		for (Class<? extends Throwable> mappedException : this.mappedMethods.keySet()) {
			if (mappedException.isAssignableFrom(exceptionType)) {
				matches.add(mappedException);
			}
		}
		if (!matches.isEmpty()) {
			//如果有多个方法可以匹配来处理异常,则进行排序
			if (matches.size() > 1) {
				matches.sort(new ExceptionDepthComparator(exceptionType));
			}
			//获取第一个方法来处理异常,返回方法
			return this.mappedMethods.get(matches.get(0));
		}
		else {
			return NO_MATCHING_EXCEPTION_HANDLER_METHOD;
		}
	}

到此为止, 已经获取到对应的处理异常的方法了, 在上面的doResolveHandlerMethodException方法中, 最后通过反射调用该方法, 处理异常

接下来讨论一下ExceptionHandlerExceptionResolver类里面的exceptionHandlerCache和exceptionHandlerAdviceCache初始化和
ExceptionHandlerMethodResolver类中的mappedMethods初始化

ExceptionHandlerExceptionResolver初始化

ExceptionHandlerExceptionResolver实现了InitializingBean接口, 猜测初始化肯定在afterPropertiesSet()方法中, 我们至于要查找ExceptionHandlerExceptionResolver是如何注入到springboot容器中的, 及afterPropertiesSet()方法逻辑即可
在这里插入图片描述

ExceptionHandlerExceptionResolver注入

实际上,ExceptionHandlerExceptionResolver并不会成为bean交给Spring容器管理。但是在WebMvcConfigurationSupport初始化过程中,会手动调用afterPropertiesSet()进行默认初始化

还记得上面HandlerExceptionResolverComposite类里面有三个异常处理类吗? 看一下他们是如何注入的

在WebMvcConfigurationSupport类里面的handlerExceptionResolver()方法:

	@Bean
	public HandlerExceptionResolver handlerExceptionResolver(
			@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager) {
		List<HandlerExceptionResolver> exceptionResolvers = new ArrayList<>();
		configureHandlerExceptionResolvers(exceptionResolvers);
		//如果是空, 在下面的if里面将三个异常处理类添加到集合中
		if (exceptionResolvers.isEmpty()) {
			addDefaultHandlerExceptionResolvers(exceptionResolvers, contentNegotiationManager);
		}
		extendHandlerExceptionResolvers(exceptionResolvers);
		HandlerExceptionResolverComposite composite = new HandlerExceptionResolverComposite();
		composite.setOrder(0);
		//赋值属性, 最后返回
		composite.setExceptionResolvers(exceptionResolvers);
		return composite;
	}

进入到addDefaultHandlerExceptionResolvers(exceptionResolvers, contentNegotiationManager);

	protected final void addDefaultHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers,
			ContentNegotiationManager mvcContentNegotiationManager) {
		//创建ExceptionHandlerExceptionResolver 对象
		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);
		}
		//手动调用afterPropertiesSet()给属性初始化
		exceptionHandlerResolver.afterPropertiesSet();
		//添加第一个
		exceptionResolvers.add(exceptionHandlerResolver);

		ResponseStatusExceptionResolver responseStatusResolver = new ResponseStatusExceptionResolver();
		responseStatusResolver.setMessageSource(this.applicationContext);
		//添加第二个
		exceptionResolvers.add(responseStatusResolver);
		//添加第三个
		exceptionResolvers.add(new DefaultHandlerExceptionResolver());
	}

所以我们只需要看WebMvcConfigurationSupport是如何注入的就可以了, 实际上他是通过WebMvcAutoConfiguration类来间接注入的
在这里插入图片描述
而WebMvcAutoConfiguration类的注入是通过自动装配来注册的
在这里插入图片描述

ExceptionHandlerExceptionResolver中exceptionHandlerAdviceCache初始化

	@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()方法

	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);
			}
			//构建value对象, 存入到exceptionHandlerAdviceCache中
			//ExceptionHandlerMethodResolver中mappedMethods初始化逻辑也在其中
			ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(beanType);
			if (resolver.hasExceptionMappings()) {
				this.exceptionHandlerAdviceCache.put(adviceBean, resolver);
			}
			if (ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
				this.responseBodyAdvice.add(adviceBean);
			}
		}
	}

ExceptionHandlerMethodResolver中mappedMethods初始化

mappedMethods的初始化逻辑很简单, 在上面的initExceptionHandlerAdviceCache()方法中我们已经找到了带有ControllerAdvice的所有类, 将类的类型作为参数创建了ExceptionHandlerMethodResolver对象, mappedMethods的初始化就在构造方法中

进入ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(beanType) 方法

	public ExceptionHandlerMethodResolver(Class<?> handlerType) {
		//获取到handlerType类中所有的带有ExceptionHandler注解的方法,
		for (Method method : MethodIntrospector.selectMethods(handlerType, EXCEPTION_HANDLER_METHODS)) {
			//解析ExceptionHandler注解中的属性, 因为可以配置多个异常类型, 所有返回的是集合, 进行遍历
			for (Class<? extends Throwable> exceptionType : detectExceptionMappings(method)) {
				//将异常类型作为key, 方法作为value, 存入到mappedMethods中
				addExceptionMapping(exceptionType, method);
			}
		}
	}

	private List<Class<? extends Throwable>> detectExceptionMappings(Method method) {
		List<Class<? extends Throwable>> result = new ArrayList<>();
		//获取method上的ExceptionHandler注解的所有的异常类型,存入到result中, 返回
		detectAnnotationExceptionMappings(method, result);
		if (result.isEmpty()) {
			for (Class<?> paramType : method.getParameterTypes()) {
				if (Throwable.class.isAssignableFrom(paramType)) {
					result.add((Class<? extends Throwable>) paramType);
				}
			}
		}
		if (result.isEmpty()) {
			throw new IllegalStateException("No exception types mapped to " + method);
		}
		return result;
	}


	private void addExceptionMapping(Class<? extends Throwable> exceptionType, Method method) {
		Method oldMethod = this.mappedMethods.put(exceptionType, method);
		if (oldMethod != null && !oldMethod.equals(method)) {
			throw new IllegalStateException("Ambiguous @ExceptionHandler method mapped for [" +
					exceptionType + "]: {" + oldMethod + ", " + method + "}");
		}
	}

结尾

上面已经说了到逻辑代码发生异常之后, 我们自定义的异常处理类如何加载处理这些异常(同时包括系统启动时的一些系统异常), 但是如果抛出一种没有处理类的异常会怎么样呢? 下篇文章在讨论一下

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值