1.SpringMVC設計理念與DispatcherServlet
任何一個框架,都有自己特定的適用領域,框架的設計和實現,必定是為了應付該領域內許多通用的,煩瑣的、基礎的工作而生。SpringMVC作為一個表現層框架,也必須直面Web開發領域中表現層中的幾大課題,並給出自己的回答:
-
URL到框架的映射。
-
http請求參數綁定
-
http響應的生成和輸出
這三大課題,組成一個完整的web請求流程,每一個部分都具有非常廣闊的外延。SpringMVC框架對這些課題的回答又是什麽呢?
學習一個框架,首要的是要先領會它的設計思想。從抽象、從全局上來審視這個框架。其中最具有參考價值的,就是這個框架所定義的核心接口。核心接口定義了框架的骨架,也在最抽象的意義上表達了框架的設計思想。
下面我以一個web請求流程為載體,依次介紹SpringMVC的核心接口和類。
用戶在瀏覽器中,輸入了http://www.xxxx.com/aaa/bbb.ccc的地址,回車後,瀏覽器發起一個http請求。請求到達你的服務器後,首先會被SpringMVC註冊在web.xml中的前端轉發器DispatcherServlet接收,DispatcherServlet是一個標準的Servlet,它的作用是接受和轉發web請求到內部框架處理單元。
下面看一下第一個出現在你面前的核心接口,它是在org.springframework.web.servlet包中定義的HandlerMapping接口:
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 PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes";
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}
?
為了閱讀方便,我去掉了源碼中的註釋,但是我強烈建議你一定要記得去閱讀它,這樣你才能從框架的設計者口中得到最準確的關於這個類或者接口的設計說明。類中定義的幾個常量,我們先不去管它。關鍵在於這個接口中唯一的方法:
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
這個方法就算對於一個java初學者來說,也很容易理解:它只有一個類型為HttpServletRequest的參數,throws Exception的聲明表示它不處理任何類型的異常,HandlerExecutionChain是它的返回類型。
回到DispatcherServlet的處理流程,當DispatcherServlet接收到web請求後,由標準Servlet類處理方法doGet或者doPost,經過幾次轉發後,最終註冊在DispatcherServlet類中的HandlerMapping實現類組成的一個List(有點拗口)會在一個循環中被遍歷。以該web請求的HttpServletRequest對象為參數,依次調用其getHandler方法,第一個不為null的調用結果,將被返回。DispatcherServlet類中的這個遍歷方法不長,貼一下,讓大家有更直觀的了解。
/**
* Return the HandlerExecutionChain for this request.
* <p>Tries all handler mappings in order.
* @param request current HTTP request
* @return the HandlerExecutionChain, or <code>null</code> if no handler could be found
*/
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//遍歷處理器映射器
for (HandlerMapping hm : this.handlerMappings) {
if (logger.isTraceEnabled()) {
logger.trace(
"Testing handler map [" + hm + "] in DispatcherServlet with name ‘" + getServletName() + "‘");
}
//獲取HandlerExecutionChain(處理執行鏈),並返回
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
return null;
}
?
是的,第一步處理就這麽簡單的完成了。一個web請求經過處理後,會得到一個HandlerExecutionChain對象,這就是SpringMVC對URl映射給出的回答。需要留意的是,HandlerMapping接口的getHandler方法參數是HttpServletRequest,這意味著,HandlerMapping的實現類可以利用HttpServletRequest中的 所有信息來做出這個HandlerExecutionChain對象的生成”決策“。這包括,請求頭、url路徑、cookie、session、參數等等一切你從一個web請求中可以得到的任何東西(最常用的是url路徑)。
SpirngMVC的第一個擴展點,就出現在這裏。我們可以編寫任意的HandlerMapping實現類,依據任何策略來決定一個web請求到HandlerExecutionChain對象的生成。可以說,從第一個核心接口的聲明開始,SpringMVC就把自己的靈活性和野心暴露無疑:哥玩的就是”Open-Closed“。
HandlerExecutionChain這個類,就是我們下一個要了解的核心類。從名字可以直觀的看得出,這個對象是一個執行鏈的封裝。熟悉Struts2的都知道,Action對象也是被層層攔截器包裝,這裏可以做個類比,說明SpringMVC確實是吸收了Struts2的部分設計思想。
HandlerExecutionChain類的代碼不長,它定義在org.springframework.web.servlet包中,為了更直觀的理解,先上代碼。
package org.springframework.web.servlet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.springframework.util.CollectionUtils;
public class HandlerExecutionChain {
private final Object handler;
private HandlerInterceptor[] interceptors;
private List<HandlerInterceptor> interceptorList;
public HandlerExecutionChain(Object handler) {
this(handler, null);
}
public HandlerExecutionChain(Object handler, HandlerInterceptor[] interceptors) {
if (handler instanceof HandlerExecutionChain) {
HandlerExecutionChain originalChain = (HandlerExecutionChain) handler;
this.handler = originalChain.getHandler();
this.interceptorList = new ArrayList<HandlerInterceptor>();
CollectionUtils.mergeArrayIntoCollection(originalChain.getInterceptors(), this.interceptorList);
CollectionUtils.mergeArrayIntoCollection(interceptors, this.interceptorList);
}
else {
this.handler = handler;
this.interceptors = interceptors;
}
}
public Object getHandler() {
return this.handler;
}
public void addInterceptor(HandlerInterceptor interceptor) {
initInterceptorList();
this.interceptorList.add(interceptor);
}
public void addInterceptors(HandlerInterceptor[] interceptors) {
if (interceptors != null) {
initInterceptorList();
this.interceptorList.addAll(Arrays.asList(interceptors));
}
}
private void initInterceptorList() {
if (this.interceptorList