在Spring MVC啓動章節http://www.javashuo.com/article/p-wnqizjsh-kr.html,介紹到了DispatcherServlet的onRefresh方法調用initStrategies方法,初始Spring MVC九大策略解析器,本章在此基礎上分析初始化HandlerMapping組件過程,本系列文章是基於Spring5.0.5RELEASE。前端
HandlerMapping接口做用是將請求映射處處理程序,以及預處理和處理後的攔截器列表,映射是基於一些標準的,其中的細節因不一樣的實現而不相同。這是官方文檔上一段描述,該接口只有一個方法getHandler(request),返回一個HandlerExecutionChain對象,接口自己很簡單,源碼以下:git
public interface HandlerMapping { 省略屬性... // 返回請求的一個處理程序handler和攔截器interceptors @Nullable HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception; }
初始化HandlerMapping的入口方法是DispatcherServlet的initHandlerMappings(ApplicationContext context)方法,該方法源碼以下:github
private void initHandlerMappings(ApplicationContext context) { this.handlerMappings = null; // detectAllHandlerMappings默認爲true,可經過DispatcherServlet的init-param參數進行設置 if (this.detectAllHandlerMappings) { // 在ApplicationContext中找到全部的handlerMapping,包括父上下文。 Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false); if (!matchingBeans.isEmpty()) { this.handlerMappings = new ArrayList<>(matchingBeans.values()); // 對handlerMapping排序,可經過指定order屬性進行設置,order的值爲int型,數越小優先級越高 AnnotationAwareOrderComparator.sort(this.handlerMappings); } } // detectAllHandlerMappings=false時 else { try { // 從ApplicationContext上下文中取id(或name)="handlerMapping"的bean HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class); // 將hm轉換成list,並賦值給屬性handlerMappings this.handlerMappings = Collections.singletonList(hm); } catch (NoSuchBeanDefinitionException ex) { // Ignore, we'll add a default HandlerMapping later. } } // 從context上下文中定義HandlerMapping時,Spring MVC將使用默認HandlerMapping,默認的HandlerMapping在DispatcherServlet.properties屬性文件中定義, // 該文件是在DispatcherServlet的static靜態代碼塊中加載的 // 默認的是:BeanNameUrlHandlerMapping和RequestMappingHandlerMapping if (this.handlerMappings == null) { this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class); if (logger.isDebugEnabled()) { logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default"); } } }
至此,咱們基本分析了HandlerMapping的初始化過程,接下來針對分析中提到的點進行驗證,以檢驗咱們的分析是正確的。web
代碼分析spring
pom配置文件segmentfault
引入Spring MVC支持,代碼以下:mvc
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.0.5.RELEASE</version> </dependency>
spring-servlet.xml是Spring MVC的配置文件,在驗證過程當中會修改此文件內容,內容根據驗證目的會有改變,具體參見驗證場景的配置。app
web配置文件async
web.xml是部署描述文件,主要配置了Spring MVC的DispatcherServlet,代碼以下:ide
<servlet> <!-- Servlet名稱,可任意定義,但必須與servlet-mapping中對應 --> <servlet-name>dispatcher</servlet-name> <!-- 指定Spring MVC核心控制類,即J2EE規範中的前端控制器(Front Controller) --> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- 指定Spring MVC配置文件,默認在WEB-INF目錄下,切名字爲[servlet-name]-servlet.xml,此文件中配置web相關內容,好比:指定掃描Controller路徑、配置邏輯視圖前綴後綴、上傳文件等等 --> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-servlet.xml</param-value> </init-param> <!-- 此配置的值爲正整數時,表示容器啓動時初始化,即調用Servlet的init方法 --> <load-on-startup>1</load-on-startup> <async-supported>true</async-supported> </servlet> <!-- 定義servlet映射 --> <servlet-mapping> <!-- 與servlet中servlet-name對應 --> <servlet-name>dispatcher</servlet-name> <!-- 映射全部的url --> <url-pattern>/</url-pattern> </servlet-mapping>
Controller控制器
本例使用的是SimpleUrlHandlerMapping映射處理器,因此咱們的Controller會繼承org.springframework.web.servlet.mvc.Controller
類,並實現handlerRequest方法,代碼以下:
public class DemoController implements Controller{ @Nullable @Override public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { request.getServletContext().log("進入Controller(Handler)處理器。。。"); return null; } }
驗證
在咱們沒有對HandlerMapping進行配置時,Spring會使用默認的HandlerMapping策略,此時咱們的Spring配置文件(spring-servlet.xml)沒有任何內容,此時咱們啓動應用,經過斷點方式驗證,結果以下:
Spring MVC默認配置爲:
從結果可知,在未進行任何配置HandlerMapping時,系統使用(支持)默認的BeanNameUrlHandlerMapping和RequestMappingHandlerMapping映射解析器。
detectAllHandlerMappings
該參數是boolean類型,做用是檢查全部的HandlerMappings映射解析器或使用id或name爲"handlerMappping"的bean,默認爲true,即從context上下文中檢查全部的HandlerMapping。
咱們先經過修改此參數爲false,即在web.xml配置DispatcherServlet時,設置init-param參數,代碼以下:
<!-- 使用id或name爲handlerMapping的映射處理器 --> <init-param> <param-name>detectAllHandlerMappings</param-name> <param-value>false</param-value> </init-param>
在Spring配置文件spring-servlet.xml中定義映射處理器,以SimpleUrlHandlerMapping爲例,代碼以下:
<!-- 定義映射處理器 --> <bean id="handlerMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="urlMap"> <props> <prop key="/demo">demoController</prop> </props> </property> <!-- 設置順序,在多個映射處理器時用於排序,可不設置 --> <property name="order" value="1"/> </bean> <bean id="demoController" class="com.github.dalianghe.controller.DemoController"/>
打上斷點,以debug模式啓動服務,結果以下:
從結果可知,Spring取到了咱們配置的HandlerMapping(SimpleUrlHandlerMapping)並轉換爲List賦值給屬性參數handlerMappings。
加載全部映射處理器
此場景須要咱們配置多個映射處理器,Spring配置文件代碼以下:
<bean id="handlerMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="urlMap"> <props> <prop key="/demo">demoController</prop> </props> </property> <property name="order" value="1"/> </bean> <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"> <property name="order" value="0"/> </bean>
注意將web.xml配置的DispatcherServlet的detectAllHandlerMappings註釋掉或設置爲true
打上斷點,以debug模式啓動服務,結果以下:
從兩種截圖中可知,Spring取出了咱們配置的全部的映射解析器,對比兩種圖,可知通過排序,Spring實現了咱們指定的解析器的order。
本小節分析了HandlerMapping的初始化,過程主要以SimpleUrlHandlerMapping實現類爲例,替換成其餘的實現類,啓動過程也是同樣的,後續會對實現類逐個進行分析,但願本章對你們能有幫助。
最後建立了qq羣方便你們交流,可掃描加入,共同窗習、共同進步,謝謝!