javascript
SpringMVC之源码分析--HandlerMapping(一)
概述
在Spring MVC啟動章節https://segmentfault.com/a/1190000014674239,介紹到了DispatcherServlet的onRefresh方法調用initStrategies方法,初始Spring MVC九大策略解析器,本章在此基礎上分析初始化HandlerMapping組件過程,本系列文章是基于Spring5.0.5RELEASE。
接口
HandlerMapping接口作用是將請求映射到處理程序,以及預處理和處理后的攔截器列表,映射是基于一些標準的,其中的細節因不同的實現而不相同。這是官方文檔上一段描述,該接口只有一個方法getHandler(request),返回一個HandlerExecutionChain對象,接口本身很簡單,源碼如下:
public interface HandlerMapping {省略屬性...// 返回請求的一個處理程序handler和攔截器interceptors@NullableHandlerExecutionChain getHandler(HttpServletRequest request) throws Exception; }初始化
初始化HandlerMapping的入口方法是DispatcherServlet的initHandlerMappings(ApplicationContext context)方法,該方法源碼如下:
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"的beanHandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);// 將hm轉換成list,并賦值給屬性handlerMappingsthis.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和RequestMappingHandlerMappingif (this.handlerMappings == null) {this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);if (logger.isDebugEnabled()) {logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");}} }至此,我們基本分析了HandlerMapping的初始化過程,接下來針對分析中提到的點進行驗證,以檢驗我們的分析是正確的。
實戰
- 項目結構
-
代碼分析
-
pom配置文件
引入Spring MVC支持,代碼如下:
<dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.0.5.RELEASE</version> </dependency> - spring配置文件
spring-servlet.xml是Spring MVC的配置文件,在驗證過程中會修改此文件內容,內容根據驗證目的會有改變,具體參見驗證場景的配置。
-
web配置文件
web.xml是部署描述文件,主要配置了Spring MVC的DispatcherServlet,代碼如下:
<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@Overridepublic 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群方便大家交流,可掃描加入,共同學習、共同進步,謝謝!
總結
以上是生活随笔為你收集整理的SpringMVC之源码分析--HandlerMapping(一)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Nginx 常用配置(学习笔记三)
- 下一篇: How is javascript as