SpringMVC是目前J2EE平臺的主流Web框架,不熟悉的園友能夠看SpringMVC源碼閱讀入門,它交代了SpringMVC的基礎知識和源碼閱讀的技巧html
本文將經過源碼分析,弄清楚SpringMVC如何找到咱們定義的Controllerweb
org.springframework.web包有49017行代碼,咱們不可能去逐行閱讀,那麼,如何尋找源碼的蹤影?spring
順着上篇博文的思路回到DispatcherServlet類的doDispatch方法,咱們獲取到了HandlerExecutionChainjson
點開getHandler方法,發現HandlerExecutionChain是經過HandlerMapping獲取的。數組
DispatcherServlet分析參見SpringMVC源碼閱讀:核心分發器DispatcherServlet瀏覽器
在943行,咱們在這裏獲取HandlerAdapter,獲取到各類argumentResolvers,用來解析參數,還能獲取到各類returnValueHandlersmvc
對HandlerMapping ctrl+h查看類繼承關係,找到RequestMappingHandlerMappingapp
咱們找到了核心類RequestMappingHandlerMapping,註釋說,該類用於建立@RequestMapping和@Controller註解的實例,在3.1版本加入,這正是咱們所須要的框架
根據官方文檔提示,咱們在調用RequestMappingHandlerMapping的時候真正調用的是HandlerMethod,打開HandlerMethod(注意是org.springframework.web包下)函數
HandlerMethod是3.1版本引入的,爲參數、返回值和註解提供便捷的封裝
打開HandlerMethod的子類InvocableHandlerMethod,發現有WebDataBinderFactory,HandlerMethodArgumentResolverComposite,ParameterNameDiscoverer,這三個屬性顯然是用來處理請求參數的
再打開InvocableHandlerMethod的子類ServletInvocableHandlerMethod,發現有HandlerMethodReturnValueHandlerComposite,這個屬性顯然是用來處理響應、返回值的
打開HandlerAdpter的子類RequestMappingHandlerAdapter,咱們看看它到底作了什麼
實例化ServletInvocableHandlerMethod,注入HandlerMethodArgumentResolverComposite,HandlerMethodReturnValueHandlerComposite和ParameterNameDiscoverer,在4.2版本之前,ServletInvocableHandlerMethod在createRequestMappingMethod方法中實例(已廢棄,該方法已刪除)
如今咱們看看HandlerMethod子類InvocableHandlerMethod到底作了什麼,瀏覽器輸入http://localhost:8080/springmvcdemo/employee/detail/1
@RequestMapping(method = RequestMethod.GET, value = "/detail/{employeeId}", produces={"application/json; charset=UTF-8"}) @ResponseBody public ModelAndView detail(@PathVariable Integer employeeId, ModelAndView view) { view.setViewName("employee/form"); view.addObject("employee", employeeService.getById(employeeId)); view.addObject("depts", deptService.listAll()); return view; }
147行獲取參數詳細信息,如參數名稱,148行以後解析參數值Value
parameters在MethodParameter初始化完畢後的數據以下
咱們有Integer和ModelAndView類型的兩個參數,第一個參數指明瞭名稱叫作"employeeId",第二個未指明名稱,因此爲null。如今,咱們打開MethodParameter類,看看這些參數信息是從哪裏來的
在MethodParameter類裏,咱們看到了參數的原始形態,僅有parameterIndex和nestingLevel。parameterIndex=0表示第一個參數,等於2表示第二個參數,以此類推
這裏定義了拷貝構造函數,初始化參數的詳細信息,獲取每一個屬性的方法園友能夠自行打斷點查看,再也不逐個贅述。
再回到InvocableHandlerMethod類,回到148行,咱們看看參數值是如何獲取的,先看args是什麼
不難理解,第一個參數"employeeId"=1,第二個參數是null,繼續往下走
148行聲明Object數組,存儲參數值,151行初始化ParameterNameDiscovery,僅爲以後方法調用可使用discover,並未真正解析參數
158~159行,調用HandlerMethodArgumentResolverComposite解析並獲得參數值
回到RequestMappingHandlerMapping的父類AbstractHandlerMethodMapping,咱們看看initHandlerMethods方法作了什麼,啓動服務會進入該方法
197~199行初始化全部Bean並獲取beanName
205行獲取Bean類型
213行isHandler判斷bean類型是否是RequestMapping或者Controller,在子類RequestMappingHandlerMapping實現
214行尋找HandlerMethod,218行調用全部被偵測到的HandlerMethod
點開detectHandlerMethods方法,看看它具體作了什麼
226~228利用反射依次獲取Controller,打斷點我發現handlerType和userType內容是一致的,getUserClass方法註釋說明是爲了獲取指定handlerType的類,我以爲這一步畫蛇添足
230行依次獲取Controller全部方法
235行getMappingForMethod方法由當前類(AbstractHandlerMethodMapping)的子類RequestMappingHandlerMapping的實現,返回RequestMappingInfo
248行遍歷methods,取出方法,249行泛型T是RequestMappingInfo
250行註冊HandlerMethod
打破砂鍋弄到底,繼續進入getMappingForMethod方法,ctrl+alt+b快速跳轉到子類實現
再進入createRequestMappingInfo方法
205行依次獲取Controller的方法上@RequestMapping註解的屬性
208行requestMapping不爲空則調用createRequestMappingInfo重載方法(兩個參數)用來構造RequestMappingInfo
208行點進去createRequestMappingInfo重載方法,一探究竟
繼續深刻,進入RequestMappingInfo,看這個類在作什麼,按ctrl+alt+u,看類的接口實現關係
看來,RequestMappingInfo實現了RequestCondition,在RequestMappingInfo類中咱們觀察combine方法,它把路徑、方法、參數、頭等信息進行了combine操做,combine是指將兩個或多個實例根據規則進行組合
打開RequestCondition子類PatternsRequestCondition,找到combine方法,在169行ctrl+alt+b跳轉至實現類AntPathMatcher combine方法,此方法初始化纔會進入
550行以"/*"結尾,截取再拼接,做者註釋很清楚,不贅述
556行以"/**"結尾,直接拼接,請看註釋
其餘的AbstractRequestCondition子類再也不介紹,園友能夠本身斷點調試看看
寫一個Controller
@Controller @RequestMapping("wildcard") public class TestWildcardController { @RequestMapping("/test/**") @ResponseBody public String test1(ModelAndView view) { view.setViewName("/test/test"); view.addObject("attr", "TestWildcardController -> /test/**"); return String.valueOf(view); } @RequestMapping("/test/*") @ResponseBody public String test2(ModelAndView view) { view.setViewName("/test/test"); view.addObject("attr", "TestWildcardController -> /test*"); return String.valueOf(view); } @RequestMapping("test?") @ResponseBody public String test3(ModelAndView view) { view.setViewName("/test/test"); view.addObject("attr", "TestWildcardController -> test?"); return String.valueOf(view); } }
輸入http://localhost:8080/springmvcdemo/wildcard/test/fff111,結果以下,進入@RequestMapping("/test/*")
輸入http://localhost:8080/springmvcdemo/wildcard/test/fff111/sss,結果以下,進入@RequestMapping("/test/**")
根據咱們剛纔源碼分析,輸入/test/fff111,AntPathMatcher combine方法幫咱們"/test/*".substring(0,6),而後和fff111 concat最終成爲/test/fff111
輸入/test/fff111/sss,就簡單了,直接concat,也就是說,test後面能夠加上任意個參數
在瀏覽器輸入http://localhost:8080/springmvcdemo/wildcard/test2,進入@RequestMapping("/test?")
test?,只能匹配一個,輸入test會直接進入/test/**
在HandlerExecutionChain 67行獲取到咱們訪問的Controller的HandlerMethod,詳細過程參照源碼分析部分
MethodParameter封裝方法參數,HandlerMethod實例化的時候初始化MethodParameter數組,再交由合適的HandlerMethodArgumentResolver(HandlerMethodArgumentResolverComposite的父類)處理
RequestCondition處理請求映射關係,使用combine方法合併不一樣的條件,getMatchingConditon方法獲取映射,compareTo方法爲映射排序
RequestMappingInfo實現RequestCondition,集中將各類RequestCondition進行combine,getMatchingConditon,compareTo
RequestMappingHandlerMapping處理請求與HandlerMethod映射關係,找出@RequestMapping和@Controller修飾的類和方法
https://docs.spring.io/spring/docs/4.3.7.RELEASE/spring-framework-reference/htmlsingle/#mvc-servlet