javascript
SpringMVC之源码分析--HandlerMapping(一)
概述
在Spring MVC啟動(dòng)章節(jié)https://segmentfault.com/a/1190000014674239,介紹到了DispatcherServlet的onRefresh方法調(diào)用initStrategies方法,初始Spring MVC九大策略解析器,本章在此基礎(chǔ)上分析初始化HandlerMapping組件過(guò)程,本系列文章是基于Spring5.0.5RELEASE。
接口
HandlerMapping接口作用是將請(qǐng)求映射到處理程序,以及預(yù)處理和處理后的攔截器列表,映射是基于一些標(biāo)準(zhǔn)的,其中的細(xì)節(jié)因不同的實(shí)現(xiàn)而不相同。這是官方文檔上一段描述,該接口只有一個(gè)方法getHandler(request),返回一個(gè)HandlerExecutionChain對(duì)象,接口本身很簡(jiǎn)單,源碼如下:
public interface HandlerMapping {省略屬性...// 返回請(qǐng)求的一個(gè)處理程序handler和攔截器interceptors@NullableHandlerExecutionChain getHandler(HttpServletRequest request) throws Exception; }初始化
初始化HandlerMapping的入口方法是DispatcherServlet的initHandlerMappings(ApplicationContext context)方法,該方法源碼如下:
private void initHandlerMappings(ApplicationContext context) {this.handlerMappings = null;// detectAllHandlerMappings默認(rèn)為true,可通過(guò)DispatcherServlet的init-param參數(shù)進(jìn)行設(shè)置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());// 對(duì)handlerMapping排序,可通過(guò)指定order屬性進(jìn)行設(shè)置,order的值為int型,數(shù)越小優(yōu)先級(jí)越高AnnotationAwareOrderComparator.sort(this.handlerMappings);}}// detectAllHandlerMappings=false時(shí)else { try {// 從ApplicationContext上下文中取id(或name)="handlerMapping"的beanHandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);// 將hm轉(zhuǎn)換成list,并賦值給屬性handlerMappingsthis.handlerMappings = Collections.singletonList(hm);}catch (NoSuchBeanDefinitionException ex) {// Ignore, we'll add a default HandlerMapping later.}}// 從context上下文中定義HandlerMapping時(shí),Spring MVC將使用默認(rèn)HandlerMapping,默認(rèn)的HandlerMapping在DispatcherServlet.properties屬性文件中定義,// 該文件是在DispatcherServlet的static靜態(tài)代碼塊中加載的// 默認(rèn)的是: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的初始化過(guò)程,接下來(lái)針對(duì)分析中提到的點(diǎn)進(jìn)行驗(yàn)證,以檢驗(yàn)我們的分析是正確的。
實(shí)戰(zhàn)
- 項(xiàng)目結(jié)構(gòu)
-
代碼分析
-
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的配置文件,在驗(yàn)證過(guò)程中會(huì)修改此文件內(nèi)容,內(nèi)容根據(jù)驗(yàn)證目的會(huì)有改變,具體參見驗(yàn)證場(chǎng)景的配置。
-
web配置文件
web.xml是部署描述文件,主要配置了Spring MVC的DispatcherServlet,代碼如下:
<servlet><!-- Servlet名稱,可任意定義,但必須與servlet-mapping中對(duì)應(yīng) --><servlet-name>dispatcher</servlet-name><!-- 指定Spring MVC核心控制類,即J2EE規(guī)范中的前端控制器(Front Controller) --><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><!-- 指定Spring MVC配置文件,默認(rèn)在WEB-INF目錄下,切名字為[servlet-name]-servlet.xml,此文件中配置web相關(guān)內(nèi)容,比如:指定掃描Controller路徑、配置邏輯視圖前綴后綴、上傳文件等等 --><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:spring-servlet.xml</param-value></init-param><!-- 此配置的值為正整數(shù)時(shí),表示容器啟動(dòng)時(shí)初始化,即調(diào)用Servlet的init方法 --><load-on-startup>1</load-on-startup><async-supported>true</async-supported> </servlet> <!-- 定義servlet映射 --> <servlet-mapping><!-- 與servlet中servlet-name對(duì)應(yīng) --><servlet-name>dispatcher</servlet-name><!-- 映射所有的url --><url-pattern>/</url-pattern> </servlet-mapping> -
Controller控制器
本例使用的是SimpleUrlHandlerMapping映射處理器,所以我們的Controller會(huì)繼承org.springframework.web.servlet.mvc.Controller類,并實(shí)現(xiàn)handlerRequest方法,代碼如下:
public class DemoController implements Controller{@Nullable@Overridepublic ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {request.getServletContext().log("進(jìn)入Controller(Handler)處理器。。。");return null;} }
-
-
驗(yàn)證
- 默認(rèn)映射處理器
在我們沒有對(duì)HandlerMapping進(jìn)行配置時(shí),Spring會(huì)使用默認(rèn)的HandlerMapping策略,此時(shí)我們的Spring配置文件(spring-servlet.xml)沒有任何內(nèi)容,此時(shí)我們啟動(dòng)應(yīng)用,通過(guò)斷點(diǎn)方式驗(yàn)證,結(jié)果如下:
Spring MVC默認(rèn)配置為:
從結(jié)果可知,在未進(jìn)行任何配置HandlerMapping時(shí),系統(tǒng)使用(支持)默認(rèn)的BeanNameUrlHandlerMapping和RequestMappingHandlerMapping映射解析器。
-
detectAllHandlerMappings
該參數(shù)是boolean類型,作用是檢查所有的HandlerMappings映射解析器或使用id或name為"handlerMappping"的bean,默認(rèn)為true,即從context上下文中檢查所有的HandlerMapping。
我們先通過(guò)修改此參數(shù)為false,即在web.xml配置DispatcherServlet時(shí),設(shè)置init-param參數(shù),代碼如下:
<!-- 使用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><!-- 設(shè)置順序,在多個(gè)映射處理器時(shí)用于排序,可不設(shè)置 --><property name="order" value="1"/> </bean><bean id="demoController" class="com.github.dalianghe.controller.DemoController"/>打上斷點(diǎn),以debug模式啟動(dòng)服務(wù),結(jié)果如下:
從結(jié)果可知,Spring取到了我們配置的HandlerMapping(SimpleUrlHandlerMapping)并轉(zhuǎn)換為L(zhǎng)ist賦值給屬性參數(shù)handlerMappings。
-
加載所有映射處理器
此場(chǎng)景需要我們配置多個(gè)映射處理器,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注釋掉或設(shè)置為true
打上斷點(diǎn),以debug模式啟動(dòng)服務(wù),結(jié)果如下:
從兩種截圖中可知,Spring取出了我們配置的所有的映射解析器,對(duì)比兩種圖,可知經(jīng)過(guò)排序,Spring實(shí)現(xiàn)了我們指定的解析器的order。
- 默認(rèn)映射處理器
總結(jié)
本小節(jié)分析了HandlerMapping的初始化,過(guò)程主要以SimpleUrlHandlerMapping實(shí)現(xiàn)類為例,替換成其他的實(shí)現(xiàn)類,啟動(dòng)過(guò)程也是一樣的,后續(xù)會(huì)對(duì)實(shí)現(xiàn)類逐個(gè)進(jìn)行分析,希望本章對(duì)大家能有幫助。
最后創(chuàng)建了qq群方便大家交流,可掃描加入,共同學(xué)習(xí)、共同進(jìn)步,謝謝!
總結(jié)
以上是生活随笔為你收集整理的SpringMVC之源码分析--HandlerMapping(一)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Nginx 常用配置(学习笔记三)
- 下一篇: How is javascript as