首先,先簡單的說一下怎麼配置SpringMvc的攔截器。java
分兩步,第一步先定義一個類,實現HandlerInterceptor接口。web
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.xmlspring
<!-- 攔截器 --> <mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/*"/> <bean class="com.mmc.interceptor.TestInterceptor"></bean> </mvc:interceptor> </mvc:interceptors>
完工。下面分析原理tomcat
打開這個DispatcherServlet類,這個類是SpringMvc中最核心的一個類。答案就在doDispatch方法裏面:mvc
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方法。app
這些可能不少人都知道,可是咱們確定還有疑問,爲何他能調用到個人攔截器裏寫的方法呢?框架
接着往下分析。async
因而咱們點開我第一處標紅那裏的mappedHandler.applyPreHandle方法。代碼以下:ide
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(),因此在點開這個方法。post
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屬性,也就是我自定義的攔截器的對象。而後去執行我這個攔截器中的三個方法。到此攔截器的運行原理就分析完了。
有分析不對的地方請不吝指出