Spring MVC 拦截器执行时机

本文详细介绍了Spring MVC中拦截器的执行时机,从准备工作开始,包括创建拦截器、配置拦截器,到分析请求执行过程,具体探讨了preHandle、postHandle和afterCompletion三个方法的执行顺序及作用,揭示了它们在处理权限、修改请求域属性和资源释放等方面的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、准备工作

搭建好Spring MVC环境以后,我们创建一个拦截器:
名为MyInterceptor并实现HandlerInterceptor接口:
在这里插入图片描述
实现接口方法、便于观察我们只在控制台输出对应的方法名:

package com.jd.interceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

public class MyInterceptor implements HandlerInterceptor {

	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		System.out.println("preHandle");
		return true;
		//初始实现方法代码中,此处返回的是false
		//改为true的原因在下面分析时机时会详细说明
	}

	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
		System.out.println("postHandle");
	}

	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
		System.out.println("afterCompletion");
	}
}

拦截器创建好以后,我们在spring xml文件中进行启用设置:

<mvc:interceptors >
	<mvc:interceptor>
		<!-- path属性用于限制能够匹配的请求  -->
		<mvc:mapping path="/userinfo/*"/>
		<bean class="com.jd.interceptor.MyInterceptor"></bean>
	</mvc:interceptor>
</mvc:interceptors>

controller方法:
在这里插入图片描述
主页面请求链接:
在这里插入图片描述
目标页面:
在这里插入图片描述
发送请求:
在这里插入图片描述

二、拦截器执行时机

收到请求后DispatcherServlet类对象如下:
执行FrameworkServlet类service方法、
执行HttpServlet类service方法、
执行FrameworkServlet类doGet方法、
执行FrameworkServlet类processRequest方法、
执行DispatcherServlet类doService方法、
(详情参见Spring MVC请求执行过程
执行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);

			/*mappedHandler = getHandler(processedRequest)
			  根据request得到一个HandlerExecutionChain对象
			  包含了mvc模块的拦截器即handlerInterceptor和真正处理请求的handler*/
			mappedHandler = getHandler(processedRequest);
			if (mappedHandler == null || mappedHandler.getHandler() == null) {
				noHandlerFound(processedRequest, response);
				return;
			}
			HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
			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;
				}
			}
			
			/*1、调用applyPreHandle方法,可见此if分支在下面语句之前
			  mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
			  所以此方法是在执行controller方法前执行*/
			if (!mappedHandler.applyPreHandle(processedRequest, response)) {
				return;
			}
			/*经过applyPreHandle方法(和拦截器preHandle方法)后
			  执行handle方法,完成controller方法
			 (这一行执行完我们会看到控制台输出controller)*/
			mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
			if (asyncManager.isConcurrentHandlingStarted()) {
				return;
			}
			applyDefaultViewName(processedRequest, mv);
			/*2、完成controller方法后,调用applyPostHandle方法,可见此方法在语句
			      processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
			     前执行*/
			mappedHandler.applyPostHandle(processedRequest, response, mv);
		}
		catch (Exception ex) {
			dispatchException = ex;
		}
		catch (Throwable err) {
			dispatchException = new NestedServletException("Handler dispatch failed", err);
		}
		/*执行完applyPostHandle方法(和拦截器postHandle方法)后
		  调用processDispatchResult方法*/
		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()) {
			if (mappedHandler != null) {			
				mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
			}
		}
		else {
			if (multipartRequestParsed) {
				cleanupMultipart(processedRequest);
			}
		}
	}
}
/*跳转自doDispatch方法
	processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
---------------------------------------*/
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) {
			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);
		}
	}
	if (mv != null && !mv.wasCleared()) {
		/*执行render方法,可见render方法是在执行完
		  applyPostHandle方法(和拦截器postHandle方法)后执行。
		  这一步执行完,响应的视图将处理完毕*/
		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()) {
		return;
	}

	if (mappedHandler != null) {
		/*3、在render方法执行完后,执行triggerAfterCompletion方法*/
		mappedHandler.triggerAfterCompletion(request, response, null);
	}
}

1、执行HandlerExecutionChain类中的applyPreHandle方法

/*跳转自doDispatch方法
   if(!mappedHandler.applyPreHandle(processedRequest, response))
--------------------------------------------------*/
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
	//获取拦截器
	HandlerInterceptor[] interceptors = getInterceptors();
	//判断拦截器是否为空
	if (!ObjectUtils.isEmpty(interceptors)) {
		//遍历拦截器
		for (int i = 0; i < interceptors.length; i++) {
			//获取到我们的MyInterceptor拦截器
			HandlerInterceptor interceptor = interceptors[i];
			/*此时interceptor即为MyInterceptor类上转型对象
			  调用preHandle时出现多态现象
			  执行我们MyInterceptor类中的preHandle方法,并返回一个布尔类型结果
			 (这一行执行完我们就会看到控制台输出preHandle)*/
			if (!interceptor.preHandle(request, response, this.handler)) {
				triggerAfterCompletion(request, response, null);
				return false;
			}
			this.interceptorIndex = i;
		}
	}
	return true;
}

可见

  • 根据代码分析如果我们返回false,applyPreHandle方法if(!interceptor.preHandle(request, response, this.handler))为true,则进入if分支最后返回false,则doDispatch方法中if(!mappedHandler.applyPreHandle(processedRequest, response))判断为true进入if分支将直接结束doDispatch方法。
  • 所以我们可以根据需要进行处理,设置返回的值。
  • 由上述:preHandle在Handler Method之前被调用,常用于实现权限。

2、执行HandlerExecutionChain类中的applyPostHandle方法

/*跳转自doDispatch方法
   mappedHandler.applyPostHandle(processedRequest, response, mv);
--------------------------------------------------*/
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception {
	//获取拦截器
	HandlerInterceptor[] interceptors = getInterceptors();
	//判断拦截器是否为空
	if (!ObjectUtils.isEmpty(interceptors)) {
		for (int i = interceptors.length - 1; i >= 0; i--) {
			//获取到我们的MyInterceptor拦截器
			HandlerInterceptor interceptor = interceptors[i];
			/*此时interceptor即为MyInterceptor类上转型对象
			  调用postHandle时出现多态现象
			  执行我们MyInterceptor类中的postHandle方法
			 (这一行执行完我们就会看到控制台输出postHandle)*/
			interceptor.postHandle(request, response, this.handler, mv);
		}
	}
}

可见

  • postHandle方法在controller方法执行后,processDispatchResult方法(和render)方法执行前执行
  • controller方法执行后的视图将传入postHandle
  • 由上述,postHandle方法常用于对请求域中的属性或视图做出修改

3、执行HandlerExecutionChain类中的triggerAfterCompletion方法

/*跳转自processDispatchResult方法
   mappedHandler.triggerAfterCompletion(request, response, null);
--------------------------------------------------*/
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex)
	throws Exception {
	//获取拦截器
	HandlerInterceptor[] interceptors = getInterceptors();
	//判断拦截器是否为空
	if (!ObjectUtils.isEmpty(interceptors)) {
		for (int i = this.interceptorIndex; i >= 0; i--) {
			//获取到我们的MyInterceptor拦截器
			HandlerInterceptor interceptor = interceptors[i];
			try {
				/*此时interceptor即为MyInterceptor类上转型对象
			      调用afterCompletion时出现多态现象
			  	  执行我们afterCompletion类中的afterCompletion方法
			 	 (这一行执行完我们就会看到控制台输出afterCompletion)*/
				interceptor.afterCompletion(request, response, this.handler, ex);
			}
			catch (Throwable ex2) {
				logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
			}
		}
	}
}

可见

  • afterCompletion方法在render方法执行完以后执行
  • 此时响应的视图已经处理完毕
  • 所以:afterCompletion方法常用于释放资源

至此,拦截器方法执行完毕

三、结果

响应页面
在这里插入图片描述
控制台输出:
在这里插入图片描述
输出顺序即为对应方法执行顺序

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值