由HandlerInterceptor.afterCompletion引起的对DispatcherServlet异常处理分析!


该方法的签名:

void afterCompletion(
			HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception;

该方法 最后一个参数为异常信息,但是我在Controller中throw异常,页面都提示了,我却在这个方法中获取不到,WHY?看下面代码注释!

DispatcherServlet中的doDispatch方法:

	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 {
				processedRequest = checkMultipart(request);
				multipartRequestParsed = processedRequest != request;

				// Determine handler for the current request.
				mappedHandler = getHandler(processedRequest, false);
				if (mappedHandler == null || mappedHandler.getHandler() == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

				// Determine handler adapter for the current request.
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

				// Process last-modified header, if supported by the handler.
				String method = request.getMethod();
				boolean isGet = "GET".equals(method);
				if (isGet || "HEAD".equals(method)) {
					long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
					if (logger.isDebugEnabled()) {
						logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
					}
					if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
						return;
					}
				}

				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}

				try {
					// Actually invoke the handler.
					mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
				}
				finally {
					if (asyncManager.isConcurrentHandlingStarted()) {
						return;
					}
				}

				applyDefaultViewName(request, mv);
				mappedHandler.applyPostHandle(processedRequest, response, mv);
			}
			catch (Exception ex) {
				dispatchException = ex;
			}
			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); // 当请求结束后,会调用该方法,并把异常传到该方法
		}
		catch (Exception ex) {
			triggerAfterCompletion(processedRequest, response, mappedHandler, ex);  // NOTE!!!
		}
		catch (Error err) {
			triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
		}
		finally {
			if (asyncManager.isConcurrentHandlingStarted()) {
				// Instead of postHandle and afterCompletion
				mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
				return;
			}
			// Clean up any resources used by a multipart request.
			if (multipartRequestParsed) {
				cleanupMultipart(processedRequest);
			}
		}
	}

processDispatchResult方法:顾名思义,这里是处理转发结果的
	private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
			HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {

		boolean errorView = false;
                // 先判断刚才传过来的是否有异常
		if (exception != null) {
                       if (exception instanceof ModelAndViewDefiningException) { // 页面异常和我们所需的无关,一般是你JSP语法写错之类的
				logger.debug("ModelAndViewDefiningException encountered", exception);
				mv = ((ModelAndViewDefiningException) exception).getModelAndView();
			}
			else {  
				Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
				mv = processHandlerException(request, response, handler, exception);// 主要是这里,在这里获得你处理异常后返回的页面(<span style="color:#808000;">@ExceptionHandler的处理返回页面,或者XML配置文件中的页面</span>)
				errorView = (mv != null);
			}
		}

		// 获得到页面后进行渲染
		if (mv != null && !mv.wasCleared()) {
			render(mv, request, response);
			if (errorView) {
				WebUtils.clearErrorRequestAttributes(request);
			}
		}
		else {
			if (logger.isDebugEnabled()) {
				logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
						"': assuming HandlerAdapter completed request handling");
			}
		}
		if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
			// Concurrent handling started during a forward
			return;
		}
                // 如果方法不是空,则执行触发afterCompletion方法,可以看到,第三个参数是NULL,所以我们需要的Exception没传!!
		if (mappedHandler != null) {
			mappedHandler.triggerAfterCompletion(request, response, null);
		}
	}

根据上面的注释,一切看起来都是很正常的,然而我们在拦截器中的afterCompletion就是获取不到Exception信息。
关在在这个函数:processHandlerException
<pre style="background-color:#ffffff;color:#000000;font-family:'Consolas';font-size:10.5pt;"><pre name="code" class="java">		// Check registered HandlerExceptionResolvers...
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,Object handler, Exception ex) throws Exception {// Check registered HandlerExceptionResolvers...// 在这里获得异常处理的页面
ModelAndView exMv = null;for (HandlerExceptionResolver handlerExceptionResolver : this.handlerExceptionResolvers) {exMv = handlerExceptionResolver.resolveException(request, response, handler, ex);if (exMv != null) {break;}}if (exMv != null) { // 如果有页面,那么进行一些处理然后返回该页面!!!注意下面的throw ex就执行不到了!if (exMv.isEmpty()) {return null;}// We might still need view name translation for a plain error model...if (!exMv.hasView()) {exMv.setViewName(getDefaultViewName(request));}if (logger.isDebugEnabled()) {logger.debug("Handler execution resulted in exception - forwarding to resolved error view: " + exMv, ex);}WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());return exMv;}throw ex; // 如果没有页面,就throw}
 

从此我们可以看到,如果定义了页面异常处理返回的页面,那么我们在拦截器就获取不到Exception,因为在doDispath方法中没有执行
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);  // NOTE!!!

猜测可能是Spring觉得你既然已经自己处理了异常,那么这里就不给你传过去了。。。
然而DispatchServlet的源码还是不能改的,也不能用继承来扩展。
 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Spring Boot中,你可以通过实现HandlerInterceptor接口来实现拦截器。下面是一个简单的示例: 首先,创建一个实现HandlerInterceptor接口的拦截器类,例如CustomInterceptor: ```java import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; public class CustomInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 在请求处理之前进行调用(Controller方法调用之前) // 返回true才会继续向下执行,返回false取消当前请求 System.out.println("Pre-handle"); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { // 请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后) System.out.println("Post-handle"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { // 整个请求结束之后被调用,也就是在DispatcherServlet渲染了对应的视图之后执行(主要是用于进行资源清理工作) System.out.println("After completion handle"); } } ``` 接下来,配置拦截器以使其生效。在你的配置类中,例如SpringBootDemoApplication类上添加@EnableWebMvc注解,并覆盖addInterceptors方法: ```java import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration @EnableWebMvc public class WebConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new CustomInterceptor()) .addPathPatterns("/**"); // 可以配置拦截路径 } } ``` 以上代码中,我们将CustomInterceptor添加为拦截器,并使用addPathPatterns方法指定需要拦截的URL路径。你可以根据需求进行配置。 这样,当你的Spring Boot应用程序处理请求时,拦截器将在请求处理之前、之后以及整个请求结束时进行相应的操作。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值