講解HandlerExecutionChain之前,先大致了解下SpringMVC的核心開發步驟:
- 在web.xml中部署DispaterServlet,並配置springmvc.xml等文件;
- 將映射文件請求到處理器HandlerMapping;
- HandlerMapping會把請求映射為HandlerExecutionChain類型的handler對象;
- 將handler對象作為參數傳遞給HandlerAdapter的實例化對象,調用其handler方法會生成一個ModelAndView對象;
- 通過ViewResolver視圖解析器,將上一步驟中生成的ModelAndView解析為View;
- DispatcherServlet根據獲取到View,將視圖返回給用戶。
本文分為兩個部分進行講解,第一部分分析Handler, 第二部分分析Interceptor
(一) Handler
首先可以明確HandlerExecutionChain與HanderMapping關系非常緊密,HandlerExecutionChain只能通過HanderMapping接口中的唯一方法來獲得,HanderMapping接口定義如下:
package org.springframework.web.servlet;
import javax.servlet.http.HttpServletRequest;
public interface HandlerMapping {
String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping";
String BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern";
String INTROSPECT_TYPE_LEVEL_MAPPING = HandlerMapping.class.getName() + ".introspectTypeLevelMapping";
String URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables";
String MATRIX_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".matrixVariables";
String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes";
//該方法是HandlerMapping接口中的唯一方法,此方法可以利用用戶請求request中的信息來生成HandlerExecutionChain對象
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}
HandlerMapping架構設計圖如下:

可以看到HandlerMapping家族有兩個分支,分別是AbstractUrlHandlerMapping和AbstractHandlerMethodMapping,它們又統一繼承於AbstractHandlerMapping。
AbstractHandlerMapping是接口HandlerMapping的抽象實現,AbstractHandlerMapping抽象類中實現了部分方法提供給它的子類使用,它還覆了getHandler方法,源碼如下:
@Override
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// AbstractUrlHandlerMapping和AbstractHandlerMethodMapping均對getHandlerInternal(request)進行了覆寫
Object handler = getHandlerInternal(request);//該方法在本類中有定義,是一個protected型的抽象方法,
//根據給定的request查找handler,如果沒有找到handler,則返回一個null
//如果經過上步沒有獲取到handler實例,則通過本類中setDefaultHandler(Object defaultHandler)設置默認handler,然后使用getDefaultHandler獲得。
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}
return getHandlerExecutionChain(handler, request);
}
上面的getHandlerInternal在AbstractUrlHandlerMapping和AbstractHandlerMethodMapping中均有實現:
AbstractUrlHandlerMapping中的getHandlerInternal方法會根據用戶請求信息中的URL查找handler:
/** Look up a handler for the URL path of the given request. -- 通過匹配URL,將URL與handler聯系起來 */ @Override protected Object getHandlerInternal(HttpServletRequest request) throws Exception { ... //略 }
AbstractHandlerMethodMapping中的getHandlerInternal方法則會根據用戶request信息中提供的Method來查找handler:
/** Look up a handler method for the given request. -- 普遍用於@requestMaping,匹配內容將它的Method作為handler */ @Override protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception { ... //略 }
回到上文中的getHandler方法,它最終返回了一個HandlerExecutionChain,getHandlerExecutionChain(handler, request)方法屬於 AbstractHandlerMapping 類的一個受保護類型方法,該方法定義如下:
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
// 如果已經是HandlerExecutionChain,則直接使用,否則創建新的
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
if (interceptor instanceof MappedInterceptor) {
MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
//向chain中加入mappedInterceptor類型的攔截器
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
else {
// 向chain中共加入攔截器
chain.addInterceptor(interceptor);
}
}
return chain;
}
簡而言之就是:當用戶請求到到DispaterServlet中后,配置的HandlerMapping會根據用戶請求(也就是handler)會將它與所有的interceptors封裝為HandlerExecutionChain對象,HandlerExecutionChain的作用在源碼注釋部分簡要解釋如下:
Handler execution chain, consisting of handler object and any handler interceptors.
Returned by HandlerMapping's HandlerMapping.getHandler method.
可知通過HandlerMapping實例對象的getHandler方法可以獲得一個HandlerExecutionChain對象實例,該實例封裝了一個handler處理對象和一些interceptors。HandlerExecutionChain類定義代碼不長,其中所有的屬性,方法如下:
(二) Interceptor 攔截器
HandlerExecutionChain中介紹到了攔截器,那這個攔截器是何方聖神?下文來簡單介紹一下。
首先引入攔截器。
上文中指出了HandlerMapping繼承了AbstractHandlerMapping,AbstractHandlerMapping又繼承於WebApplicationObjectSupport,進而繼承了ApplicationObjectSupport。
ApplicationObjectSupport實現了ApplicationContextAware接口,在Spring容器中如果該bean類實現了ApplicationContextAware,那么通過容器獲取這個bean時,void setApplicationContext(ApplicationContext context)方法將被調用。
@Override
protected void initApplicationContext() throws BeansException {
extendInterceptors(this.interceptors);
//探測容器中所有攔截器
detectMappedInterceptors(this.mappedInterceptors);
//初始配置這些攔截器
initInterceptors();
}
其中,initInterceptors()方法如下,主要作用是返回一個HandlerInterceptor對象,然后將這個對象放到 this.adaptedInterceptors 集合中(ps: adaptedInterceptors很重要喲)。
/**
* Initialize the specified interceptors, checking for {@link MappedInterceptor}s and
* adapting {@link HandlerInterceptor}s and {@link WebRequestInterceptor}s if necessary.
* @see #setInterceptors
* @see #adaptInterceptor
*/
protected void initInterceptors() {
if (!this.interceptors.isEmpty()) {
for (int i = 0; i < this.interceptors.size(); i++) {
Object interceptor = this.interceptors.get(i);
if (interceptor == null) {
throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null");
}
this.adaptedInterceptors.add(adaptInterceptor(interceptor));
}
}
}
至此,了解了攔截器來龍,那接下來說一下攔截器的去脈。
攔截器的總接口 HandlerInterceptor 定義如下:
public interface HandlerInterceptor {
boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception;
void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception;
}
在spring-webmvc-4.3.1.RELEASE-sources.jar中,HandlerIntercetor共有13個實現類,如MappedInterceptor、webContentInterceptor、ThemeChangeInterceptor、HandlerIntercetorAdapter等,這里就不一一列出來了。
當客戶端發送請求后,DispatcherServlet.doDispatch方法中會處理請求,下面貼出doDispatch(req, res)中與攔截器相關的代碼:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
...(前面代碼略)
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.//獲取根據請求獲取handler
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.//獲取handler適配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
...(略)
// 攔截器執行攔截,對客戶端請求響應requset進行攔截
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 核心邏輯,處理handler,返回ModerAndView對象
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
// 攔截器執行攔截,對客戶端響應response進行攔截
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
...(后面代碼略)
}
}
}
這里以mappedHandler.applyPreHandle(req, res)為例,簡要分析一下doDispatch中的處理請求前攔截邏輯:
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 獲取所有初始化的攔截器
HandlerInterceptor[] interceptors = getInterceptors();
// 遍歷攔截器
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = 0; i < interceptors.length; i++) {
// 遍歷執行每個攔截器中的preHandle方法(即對某一個handler,要遍歷執行所有的攔截器preHandle方法)
HandlerInterceptor interceptor = interceptors[i];
if (!interceptor.preHandle(request, response, this.handler)) {
// 最終調用interceptor.afterCompletion(req, res, handler, ex)
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
}
return true;
}
當然也可以自定義攔截器,只要實現HandlerInterceptor 接口中的preHandle、postHandle、afterCompletion方法就可以啦。
寫在最后
其實看源碼是最好的,也是最優效果的,本文只希望能在閱讀源碼的時候提供一點參考作用就夠了。
