SpringMvc拦截器运行原理。
首先,先简单的说一下怎么配置SpringMvc的拦截器。
分两步,第一步先定义一个类,实现HandlerInterceptor接口。
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; public class TestInterceptor implements HandlerInterceptor{ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("preHandle invoke"); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("postHandle invoke"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("afterCompletion invoke"); } }
第二布,配置springMvc.xml
<!-- 拦截器 --> <mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/*"/> <bean class="com.mmc.interceptor.TestInterceptor"></bean> </mvc:interceptor> </mvc:interceptors>
完工。下面分析原理
打开这个DispatcherServlet类,这个类是SpringMvc中最核心的一个类。答案就在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); 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()) { String requestUri = urlPathHelper.getRequestUri(request); logger.debug("Last-Modified value for [" + requestUri + "] 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); } 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); } } }
里面我标红了四处,第一处对应执行自定义拦截器的preHandler方法,第二处对应执行你的Controller类中的RequestMapping映射的处理方法,第三处是自定义拦截器的postHandle方法,第四处是自定义拦截器的afterCompletion方法。
这些可能很多人都知道,但是我们肯定还有疑问,为什么他能调用到我的拦截器里写的方法呢?
接着往下分析。
于是我们点开我第一处标红那里的mappedHandler.applyPreHandle方法。代码如下:
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception { if (getInterceptors() != null) { for (int i = 0; i < getInterceptors().length; i++) { HandlerInterceptor interceptor = getInterceptors()[i]; if (!interceptor.preHandle(request, response, this.handler)) { triggerAfterCompletion(request, response, null); return false; } this.interceptorIndex = i; } } return true; }
看得出来,他是在遍历getInterceptors(),所以在点开这个方法。
public HandlerInterceptor[] getInterceptors() { if (this.interceptors == null && this.interceptorList != null) { this.interceptors = this.interceptorList.toArray(new HandlerInterceptor[this.interceptorList.size()]); } return this.interceptors; }
这里面有两个变量,interceptorList和interceptors。这时我肯定会猜想,肯定是某个时候系统框架把这两个变量赋值了,然后在我执行方法的时候都会去看看这两个变量是不是有值的。有就执行拦截器。
那么这两个变量是什么时候赋值的呢?找到定义这两个变量的地方,他们是在HandlerExecutionChain类里面,我把断点打在这个变量定义那里。debug运行,代码停在了HandlerExecutionChain里面
看debug模式中的方法执行顺序,点到doDispatch方法里面,在执行applyPreHandle即调用拦截器的方法之前,有一个getHandler方法,这个方法里面有getHandlerExecutionChain方法。这个方法里面有如下代码:
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) { HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain) ? (HandlerExecutionChain) handler : new HandlerExecutionChain(handler); chain.addInterceptors(getAdaptedInterceptors()); String lookupPath = urlPathHelper.getLookupPathForRequest(request); for (MappedInterceptor mappedInterceptor : mappedInterceptors) { if (mappedInterceptor.matches(lookupPath, pathMatcher)) { chain.addInterceptor(mappedInterceptor.getInterceptor()); } } return chain; }
注意看,里面有一句chain.addInterceptor(mappedInterceptor.getInterceptor());这个从名字就知道是添加拦截器。而这个拦截器是从mappedInterceptor获得的属性,mappedInterceptor是遍历mappedInterceptors得到的。而这个对象在我运行这个的时候就已经存在了。所以我思考是不是启动项目的时候就已经初始化了,而且是根据我的xml配置文件来初始化的。于是我在mappedInterceptors对象处打断点,启动tomcat。果然代码执行到了。在AbstractHandlerMapping类中找到了这个方法,
@Override protected void initApplicationContext() throws BeansException { extendInterceptors(this.interceptors); detectMappedInterceptors(this.mappedInterceptors); initInterceptors();
在这个detectMappedInterceptors(this.mappedInterceptors);里面便是将mappedInterceptors对象赋值了。下面我就不贴代码了。就是去找MappedInterceptor类,这个类是根据xml文件中<mvc:interceptor>的内容来构建的。构建方法在InterceptorsBeanDefinitionParser类里面。
顺着来一遍就是这样的:
启动项目,InterceptorsBeanDefinitionParser类会根据配置文件构建MappedInterceptor对象集合,当我发送请求的时候,系统去看这个集合里有没有值,如果有值,就遍历执行这个对象,取得这个对象中的HandlerInterceptor属性,也就是我自定义的拦截器的对象。然后去执行我这个拦截器中的三个方法。到此拦截器的运行原理就分析完了。
有分析不对的地方请不吝指出