SpringMVC统一异常处理源码解析

序言

在使用SpringMVC的过程中,应用系统通常都会有需要统一处理未捕获异常的需求,为了将异常处理的逻辑与业务逻辑代码分离开,SpringMVC提供了@ExceptionHandler 统一异常处理的方式。@ExceptionHandler 是配合@ControllerAdvice一起使用的,这样我们就可以在集中的地方处理未知异常,打印对应日志,封装返回结果等。

@ExceptionHandler 所处位置

首先我们知道所有的事情都是发生在DispatcherServlet这个类中,然后在initStrategies中完成了异常处理器的初始化:

	protected void initStrategies(ApplicationContext context) {
   
		initMultipartResolver(context);
		initLocaleResolver(context);
		initThemeResolver(context);
		initHandlerMappings(context);
		initHandlerAdapters(context);
		initHandlerExceptionResolvers(context); //初始化异常处理器
		initRequestToViewNameTranslator(context);
		initViewResolvers(context);
		initFlashMapManager(context);
	}

然后经过一系列的RequestMapping映射找到handler并调用后返回了结果,而这个结果有可能是一个异常对象,所以在调用handler完成后再来对异常进行了统一处理:

	protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
   
		//--- 省略一堆代码 ---
		try {
   
			//--再省略一堆代码 ---

			try {
   
				//--省略几十行代码----
				// 调用hander
				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));
		}
		finally {
   
			//---进行一些其他处理 ---
		}
	}

在processDispatchResult方法中实现了对异常的处理:

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 {
   //如果handler最终返回的是异常,则调用processHandlerException方法
				Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
				mv = processHandlerException(request, response, handler, exception);
				errorView = (mv != null);
			}
		}
		//-----省略了很多代码 -------------
}

而在processHandlerException方法中,最终会使用handlerExceptionResolvers来对异常进行处理:

	@Nullable
	protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
			@Nullable Object handler, Exception ex) throws Exception {
   
		request.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);

		ModelAndView exMv = null;
		if (this.handlerExceptionResolvers != null) {
   
		    //就在这里,使用了handlerExceptionResolvers来处理异常
			for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
   
				exMv = resolver.resolveException(request, response, handler, ex);
				if (exMv != null) {
   
					break;
				}
			}
		}
		//----省略了很多代码----------
}

统一异常处理的使用

这里我先描述一下如何使用,直接看代码:

@ControllerAdvice("com.ddd.controller")
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值