SpringMVC是目前主流的Web MVC框架之一。 jquery
咱們使用瀏覽器經過地址 http://ip:port/contextPath/path進行訪問,SpringMVC是如何得知用戶究竟是訪問哪一個Controller中的方法,這期間到底發生了什麼。數組
本文將分析SpringMVC是如何處理請求與Controller之間的映射關係的,讓讀者知道這個過程當中到底發生了什麼事情。瀏覽器
本文其實是在上文基礎上,深刻分析服務器
<pre>HandlerMapping裏的</pre>mvc
<pre>HandlerExecutionChain getHandler(HttpServletRequest var1) throws Exception;</pre>app
<pre>該方法的具體實現,包括它如何找到對應的方法,以及如何把結果保存在map裏,以便讓請求轉發到對應的handler上,同時也分析了handleradaptor具體作了什麼事情。</pre>框架
在分析源碼以前,咱們先了解一下幾個東西。函數
1.這個過程當中重要的接口和類。工具
HandlerMethod類:源碼分析
Spring3.1版本以後引入的。 是一個封裝了方法參數、方法註解,方法返回值等衆多元素的類。
它的子類InvocableHandlerMethod有兩個重要的屬性WebDataBinderFactory和HandlerMethodArgumentResolverComposite, 很明顯是對請求進行處理的。
InvocableHandlerMethod的子類ServletInvocableHandlerMethod有個重要的屬性HandlerMethodReturnValueHandlerComposite,很明顯是對響應進行處理的。
ServletInvocableHandlerMethod這個類在HandlerAdapter對每一個請求處理過程當中,都會實例化一個出來(上面提到的屬性由HandlerAdapter進行設置),分別對請求和返回進行處理。 (RequestMappingHandlerAdapter源碼,實例化ServletInvocableHandlerMethod的時候分別set了上面提到的重要屬性)
MethodParameter類:
HandlerMethod類中的parameters屬性類型,是一個MethodParameter數組。MethodParameter是一個封裝了方法參數具體信息的工具類,包括參數的的索引位置,類型,註解,參數名等信息。
HandlerMethod在實例化的時候,構造函數中會初始化這個數組,這時只初始化了部分數據,在HandlerAdapter對請求處理過程當中會完善其餘屬性,以後交予合適的HandlerMethodArgumentResolver接口處理。
以類DeptController爲例:
@Controller @RequestMapping(value = "/dept") public class DeptController { @Autowired private IDeptService deptService; @RequestMapping("/update") @ResponseBody public String update(Dept dept) { deptService.saveOrUpdate(dept); return "success"; } }
(剛初始化時的數據)
(HandlerAdapter處理後的數據)
RequestCondition接口:
Spring3.1版本以後引入的。 是SpringMVC的映射基礎中的請求條件,能夠進行combine, compareTo,getMatchingCondition操做。這個接口是映射匹配的關鍵接口,其中getMatchingCondition方法關乎是否能找到合適的映射。
RequestMappingInfo類:
Spring3.1版本以後引入的。 是一個封裝了各類請求映射條件並實現了RequestCondition接口的類。
有各類RequestCondition實現類屬性,patternsCondition,methodsCondition,paramsCondition,headersCondition,consumesCondition以及producesCondition,這個請求條件看屬性名也瞭解,分別表明http請求的路徑模式、方法、參數、頭部等信息。
RequestMappingHandlerMapping類:
處理請求與HandlerMethod映射關係的一個類。
2.Web服務器啓動的時候,SpringMVC到底作了什麼。
先看AbstractHandlerMethodMapping的initHandlerMethods方法中。
咱們進入createRequestMappingInfo方法看下是如何構造RequestMappingInfo對象的。
PatternsRequestCondition構造函數:
類對應的RequestMappingInfo存在的話,跟方法對應的RequestMappingInfo進行combine操做。
而後使用符合條件的method來註冊各類HandlerMethod。
下面咱們來看下各類RequestCondition接口的實現類的combine操做。
PatternsRequestCondition:
RequestMethodsRequestCondition:
方法的請求條件,用個set直接add便可。
其餘相關的RequestConditon實現類讀者可自行查看源碼。
最終,RequestMappingHandlerMapping中兩個比較重要的屬性
private final Map<T, HandlerMethod> handlerMethods = new LinkedHashMap<T, HandlerMethod>();
private final MultiValueMap<String, T> urlMap = new LinkedMultiValueMap<String, T>();
T爲RequestMappingInfo。
構造完成。
咱們知道,SpringMVC的分發器DispatcherServlet會根據瀏覽器的請求地址得到HandlerExecutionChain。
這個過程咱們看是如何實現的。
首先看HandlerMethod的得到(直接看關鍵代碼了):
這裏的比較器是使用RequestMappingInfo的compareTo方法(RequestCondition接口定義的)。
而後構造HandlerExecutionChain加上攔截器
寫了這麼多,來點例子讓咱們驗證一下吧。
@Controller @RequestMapping(value = "/wildcard") public class TestWildcardController { @RequestMapping("/test/**") @ResponseBody public String test1(ModelAndView view) { view.setViewName("/test/test"); view.addObject("attr", "TestWildcardController -> /test/**"); return view; } @RequestMapping("/test/*") @ResponseBody public String test2(ModelAndView view) { view.setViewName("/test/test"); view.addObject("attr", "TestWildcardController -> /test*"); return view; } @RequestMapping("test?") @ResponseBody public String test3(ModelAndView view) { view.setViewName("/test/test"); view.addObject("attr", "TestWildcardController -> test?"); return view; } @RequestMapping("test/*") @ResponseBody public String test4(ModelAndView view) { view.setViewName("/test/test"); view.addObject("attr", "TestWildcardController -> test/*"); return view; } }
因爲這裏的每一個pattern都帶了*所以,都不會加入到urlMap中,可是handlerMethods仍是有的。
當咱們訪問:http://localhost:8888/SpringMVCDemo/wildcard/test1的時候。
會先根據 "/wildcard/test1" 找urlMap對應的RequestMappingInfo集合,找不到的話取handlerMethods集合中全部的key集合(也就是RequestMappingInfo集合)。
而後進行匹配,匹配根據RequestCondition的getMatchingCondition方法。
最終匹配到2個RequestMappingInfo:
而後會使用比較器進行排序。
以前也分析過,比較器是有優先級的。
咱們看到,RequestMappingInfo除了pattern,其餘屬性都是同樣的。
咱們看下PatternsRequestCondition比較的邏輯:
所以,/test*的通配符比/test?的多,所以,最終選擇了/test?
直接比較優先於通配符。
@Controller @RequestMapping(value = "/priority") public class TestPriorityController { @RequestMapping(method = RequestMethod.GET) @ResponseBody public String test1(ModelAndView view) { view.setViewName("/test/test"); view.addObject("attr", "其餘condition相同,帶有method屬性的優先級高"); return view; } @RequestMapping() @ResponseBody public String test2(ModelAndView view) { view.setViewName("/test/test"); view.addObject("attr", "其餘condition相同,不帶method屬性的優先級高"); return view; } }
這裏例子,其餘requestCondition都同樣,只有RequestMethodCondition不同。
看出,方法多的優先級越多。
至於其餘的RequestCondition,你們自行查看源碼吧。
以上分析均是基於Controller方法的映射(RequestMappingHandlerMapping)。
SpringMVC中還有靜態文件的映射,SimpleUrlHandlerMapping。
DispatcherServlet找對應的HandlerExecutionChain的時候會遍歷屬性handlerMappings,這個一個實現了HandlerMapping接口的集合。
因爲咱們在*-dispatcher.xml中加入瞭如下配置:
<mvc:resources location="/static/" mapping="/static/**"/>
Spring解析配置文件會使用ResourcesBeanDefinitionParser進行解析的時候,會實例化出SimpleUrlHandlerMapping。
其中註冊的HandlerMethod爲ResourceHttpRequestHandler。
訪問地址:http://localhost:8888/SpringMVCDemo/static/js/jquery-1.11.0.js
地址匹配到/static/**。
最終SimpleUrlHandlerMapping找到對應的Handler -> ResourceHttpRequestHandler。
ResourceHttpRequestHandler進行handleRequest的時候,直接輸出資源文件的文本內容。
大體上整理了一下SpringMVC對請求的處理,包括其中比較關鍵的類和接口,但願對讀者有幫助。
讓本身對SpringMVC有了更深刻的認識,也爲以後分析數據綁定,攔截器、HandlerAdapter等打下基礎。