前言:
在這之前我們已經建立請求和Controller方法的映射集合,接下來我們就要去取出映射關系里獲取請求的邏輯實例。
根據請求查找對應的Controller方法的流程主要發生在DispatcherServlet類的doDispatch()方法。該方法的調用時機是當我們訪問路徑:http://localhost:8080/springmvcdemo/headline/open 的時候
觀察下面的流程圖我們可以看到
(1)會去訪問到DispatcherServlet的父類FrameworkServlet的service()方法
(2)然后會根據請求選擇doGet()或者doPost()方法
(3)然后父類會去調用到子類DispatcherServlet的doService()方法去處理邏輯
(4)DispatcherServlet的doService()方法會去調用doDispatch()去派遣給Controller執行 => (根據請求查找對應的Controller方法)就是在該方法中調用了getHandler()去獲取的
這里我們重新回到RequestMappingHandlerMapping類中。
我們觀察其類圖:
通過觀察類圖我們發現該類還實現了HandlerMapping接口,該接口就一個getHanndler()方法。該方法只需要給其傳遞HttpServletRequest參數即可返回HandlerExecutionChain對象
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
類圖上顯示HandlerMapping接口的主要實現類為:AbstractHandlerMapping類。
接下來我們跟進到AbstractHandlerMapping類的getHanndler()方法。
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { //獲取handler的具體邏輯,留給子類實現 Object handler = getHandlerInternal(request); if (handler == null) { //如果獲取到的handler為null則采用默認的handler,即屬性defaultHandler handler = getDefaultHandler(); } if (handler == null) { //如果連DefaultHandler也為空,則直接返回空 return null; } // Bean name or resolved handler? //如果handler是beanName,從容器里獲取對應的bean if (handler instanceof String) { String handlerName = (String) handler; handler = obtainApplicationContext().getBean(handlerName); } //根據handler和request獲取處理器鏈HandlerExecutionChain HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);//獲取到執行器鏈
//下面的代碼與主要流程無關(略) if (logger.isTraceEnabled()) {logger.trace("Mapped to " + handler);} else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {logger.debug("Mapped to " + executionChain.getHandler());} if (hasCorsConfigurationSource(handler)) { CorsConfiguration config = (this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(request) : null); CorsConfiguration handlerConfig = getCorsConfiguration(handler, request); config = (config != null ? config.combine(handlerConfig) : handlerConfig); executionChain = getCorsHandlerExecutionChain(request, executionChain, config); } return executionChain;//返回執行器鏈 }
(1)調用子類的getHandlerInternal()方法去獲取handler的具體邏輯(AbstractHandlerMethodMapping.getHandlerInternal()方法)
(A)獲取到Request(請求)中的URL(用來匹配handler)
(B)調用lookupHandlerMethod()方法,根據路徑尋找Handler,並封裝成HandlerMethod
(a)嘗試從先前映射建立的時候存入的urlLookup中查找映射
(b)如果匹配到了,檢查其他屬性是否符合要求,如請求方法,參數,hander等 (沒有直接匹配到,則遍歷所有的處理方法進行通配符匹配)
(c)如果方法有多個匹配,不同的通配符等,則排序選擇出最合適的一個
(d)如果有多個匹配的,會找到第二個最合適的進行比較。
(e)給請求(request)設置上屬性參數
(f)返回匹配的URL的處理的方法
(C)根據handlerMethod中的bean來實例化Handler,並添加進HandlerMethod
(2)如果獲取到的handler為null則采用默認的handler,即屬性defaultHandler
(3)如果連DefaultHandler也為空,則直接返回空
(4)如果上面獲取到的處理方法(handler)是實現了String接口,說明其只是一個beanName,此時我們要去容器中獲取到真正對應的Bean
(5)根據處理方法(handler)和請求(request)獲取處理器鏈(HandlerExecutionChain)
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) { //判斷handler是不是執行器鏈,如果不是則創建一個執行器鏈 HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ? (HandlerExecutionChain) handler : new HandlerExecutionChain(handler)); String lookupPath = this.urlPathHelper.getLookupPathForRequest(request, LOOKUP_PATH); //包裝攔截器 /*可以去做一些請求的時候業余上對用戶信息的攔截 <mvc:interceptor> <mvc:mapping path="/shopadmin/**" /> <bean id="ShopInterceptor" class="com.imooc.o2o.interceptor.shopadmin.ShopLoginInterceptor" /> </mvc:interceptor> */ for (HandlerInterceptor interceptor : this.adaptedInterceptors) { if (interceptor instanceof MappedInterceptor) { MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor; if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) { chain.addInterceptor(mappedInterceptor.getInterceptor()); } } else { chain.addInterceptor(interceptor); } } return chain;//將包裝好的執行器鏈返回
}
(A)創建獲取到執行器鏈(判斷傳入的handler(處理方法)是不是執行器鏈,如果不是則創建一個執行器鏈,如果是則強轉為執行器鏈)
(B)對獲取到的執行器鏈進行包裝攔截器(將配置好的攔截器for循環遍歷然后篩選path,如果符合當前這個執行器鏈,則對其進行包裝)
(6)無關邏輯(略)
(7)返回處理器鏈