SpringMVC做爲Struts2以後異軍突起的一個表現層框架,正愈來愈流行,相信JavaEE的開發者們就算沒使用過SpringMVC,也應該對其略有耳聞。我試圖經過對SpringMVC的設計思想和源碼實現的剖析,從抽象意義上的設計層面和實現意義上的代碼層面兩個方面,逐一揭開SpringMVC神祕的面紗,本文的代碼,都是基於Spring的 3.1.3RELEASE版本。前端
任何一個框架,都有本身特定的適用領域,框架的設計和實現,一定是爲了應付該領域內許多通用的,煩瑣的、基礎的工做而生。SpringMVC做爲一個表現層框架,也必須直面Web開發領域中表現層中的幾大課題,並給出本身的回答:java
這三大課題,組成一個完整的web請求流程,每個部分都具備很是廣闊的外延。SpringMVC框架對這些課題的回答又是什麼呢?web
學習一個框架,首要的是要先領會它的設計思想。從抽象、從全局上來審視這個框架。其中最具備參考價值的,就是這個框架所定義的核心接口。核心接口定義了框架的骨架,也在最抽象的意義上表達了框架的設計思想。spring
下面我以一個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; }
爲了閱讀方便,我去掉了源碼中的註釋,可是我強烈建議你必定要記得去閱讀它,這樣你才能從框架的設計者口中獲得最準確的關於這個類或者接口的設計說明。類中定義的幾個常量,咱們先不去管它。關鍵在於這個接口中惟一的方法:cookie
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
這個方法就算對於一個java初學者來講,也很容易理解:它只有一個類型爲HttpServletRequest的參數,throws Exception的聲明表示它不處理任何類型的異常,HandlerExecutionChain是它的返回類型。session
回到DispatcherServlet的處理流程,當DispatcherServlet接收到web請求後,由標準Servlet類處理方法doGet或者doPost,通過幾迴轉發後,最終註冊在DispatcherServlet類中的HandlerMapping實現類組成的一個List(有點拗口)會在一個循環中被遍歷。以該web請求的HttpServletRequest對象爲參數,依次調用其getHandler方法,第一個不爲null的調用結果,將被返回。DispatcherServlet類中的這個遍歷方法不長,貼一下,讓你們有更直觀的瞭解。app
/** * 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 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 == null) { this.interceptorList = new ArrayList<HandlerInterceptor>(); } if (this.interceptors != null) { this.interceptorList.addAll(Arrays.asList(this.interceptors)); this.interceptors = null; } } public HandlerInterceptor[] getInterceptors() { if (this.interceptors == null && this.interceptorList != null) { this.interceptors = this.interceptorList.toArray(new HandlerInterceptor[this.interceptorList.size()]); } return this.interceptors; } @Override public String toString() { if (this.handler == null) { return "HandlerExecutionChain with no handler"; } StringBuilder sb = new StringBuilder(); sb.append("HandlerExecutionChain with handler [").append(this.handler).append("]"); if (!CollectionUtils.isEmpty(this.interceptorList)) { sb.append(" and ").append(this.interceptorList.size()).append(" interceptor"); if (this.interceptorList.size() > 1) { sb.append("s"); } } return sb.toString(); } }
亂七八糟一大堆,相信你也沒全看完,也不必全看。其實只須要看兩行足矣。
private final Object handler; private HandlerInterceptor[] interceptors;
不出咱們所料,一個實質執行對象,還有一堆攔截器。這不就是Struts2中的實現麼,SpringMVC沒有避嫌,仍是採用了這種封裝。獲得HandlerExecutionChain這個執行鏈(execution chain)以後,下一步的處理將圍繞其展開。
HandlerInterceptor也是SpringMVC的核心接口,定義以下:
package org.springframework.web.servlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; 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; }
至此,HandlerExecutionChain整個執行脈絡也就清楚了:在真正調用其handler對象前,HandlerInterceptor接口實現類組成的數組將會被遍歷,其preHandle方法會被依次調用,而後真正的handler對象將被調用。
handler對象被調用後,就生成了須要的響應數據,在將處理結果寫到HttpServletResponse對象以前(SpringMVC稱爲渲染視圖),其postHandle方法會被依次調用。視圖渲染完成後,最後afterCompletion方法會被依次調用,整個web請求的處理過程就結束了。
在一個處理對象執行以前,以後利用攔截器作文章,這已經成爲一種經典的框架設計套路。Struts2中的攔截器會作諸如參數綁定這類複雜的工做,那麼SpringMVC的攔截器具體作些什麼呢?咱們暫且不關心,雖然這是很重要的細節,但細節畢竟是細節,咱們先來理解更重要的東西。
HandlerInterceptor,是SpringMVC的第二個擴展點的暴露,經過自定義攔截器,咱們能夠在一個請求被真正處理以前、請求被處理但還沒輸出到響應中、請求已經被輸出到響應中以後這三個時間點去作任何咱們想要作的事情。Struts2框架的成功,就是源於這種攔截器的設計,SpringMVC吸取了這種設計思想,並推陳出新,更合理的劃分了三個不一樣的時間點,從而給web請求處理這個流程,提供了更大的擴展性。
這個HandlerExecutionChain類中以Object引用所聲明的handler對象,究竟是個什麼東東?它是怎麼被調用的?
回答這些問題以前,先看SpringMVC中的又一個核心接口,HandlerAdapter:
package org.springframework.web.servlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public interface HandlerAdapter { boolean supports(Object handler); ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception; long getLastModified(HttpServletRequest request, Object handler); }
在DispatcherServlet中,除了HandlerMapping實現類的列表,一樣也註冊了一個HandlerAdapter實現類組成的列表,有代碼爲證。
/** List of HandlerMappings used by this servlet */ private List<HandlerMapping> handlerMappings; /** List of HandlerAdapters used by this servlet */ private List<HandlerAdapter> handlerAdapters;
接下來,咱們再以DispatcherServlet類中另一段代碼來回答上述的問題:
/** * Return the HandlerAdapter for this handler object. * @param handler the handler object to find an adapter for * @throws ServletException if no HandlerAdapter can be found for the handler. This is a fatal error. */ protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException { for (HandlerAdapter ha : this.handlerAdapters) { if (logger.isTraceEnabled()) { logger.trace("Testing handler adapter [" + ha + "]"); } if (ha.supports(handler)) { return ha; } } throw new ServletException("No adapter for handler [" + handler + "]: Does your handler implement a supported interface like Controller?"); }
這段代碼已經很明顯了,HandlerExecutionChain中的handler對象會被做爲參數傳遞進去,在DispatcherServlet類中註冊的HandlerAdapter實現類列表會被遍歷,而後返回第一個supports方法返回true的HandlerAdapter對象,用這個HandlerAdapter實現類中的handle方法處理handler對象,並返回ModelAndView這個包含了視圖和數據的對象。HandlerAdapter就是SpringMVC提供的第三個擴展點,你能夠提供本身的實現類來處理handler對象。
ModelAndView對象的代碼就不貼了,它是SpringMVC中對視圖和數據的一個聚合類。其中的視圖,就是由SpringMVC的最後一個核心接口View所抽象:
package org.springframework.web.servlet; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public interface View { String RESPONSE_STATUS_ATTRIBUTE = View.class.getName() + ".responseStatus"; String PATH_VARIABLES = View.class.getName() + ".pathVariables"; String getContentType(); void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception; }
全部的數據,最後會做爲一個Map對象傳遞到View實現類中的render方法,調用這個render方法,就完成了視圖到響應的渲染。這個View實現類,就是來自HandlerAdapter中的handle方法的返回結果。固然從ModelAndView到真正的View實現類有一個解析的過程,ModelAndView中能夠有真正的視圖對象,也能夠只是有一個視圖的名字,SpringMVC會負責將視圖名稱解析爲真正的視圖對象。
至此,咱們瞭解了一個典型的完整的web請求在SpringMVC中的處理過程和其中涉及到的核心類和接口。
在一個典型的SpringMVC調用中,HandlerExecutionChain中封裝handler對象就是用@Controller註解標識的類的一個實例,根據類級別和方法級別的@RequestMapping註解,由默認註冊的DefaultAnnotationHandlerMapping(3.1.3中更新爲RequestMappingHandlerMapping類,可是爲了向後兼容,DefaultAnnotationHandlerMapping也可使用)生成HandlerExecutionChain對象,再由AnnotationMethodHandlerAdapter(3.1.3中更新爲RequestMappingHandlerAdapter類,可是爲了向後兼容,AnnotationMethodHandlerAdapter也可使用)來執行這個HandlerExecutionChain對象,生成最終的ModelAndView對象後,再由具體的View對象的render方法渲染視圖。
能夠看到,做爲一個表現層框架,SpringMVC沒有像Struts2那樣激進,並無採用和Web容器徹底解耦的設計思想,而是以原生的Servlet框架對象爲依託,經過合理的抽象,制定了嚴謹的的處理流程。這樣作的結果是,執行效率比Struts2要高,靈活性也上升了一個層次。