SpringMVC之源碼分析--HandlerMapping(一)

概述

在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配置文件

      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羣方便你們交流,可掃描加入,共同窗習、共同進步,謝謝!

相關文章
相關標籤/搜索