上一篇咱們講解了DispatcherServlet的url-pattern配置詳解,今天咱們來真正對SpringMVC進行配置式開發。
所謂配置式開發是指「處理器類是程序員本身定義的、實現了特定接口的類,而後在SpringMVC配置文件中對該類進行顯式的,明確的註冊」的開發方式。今天咱們的開發仍是將中央調度器的url-pattern配置成*.do。而後將springmvc.xml的靜態資源訪問先取消。程序員
1、處理器映射器(BeanNameUrlHandlerMapping)
handlerMapping接口負責根據request請求找到對應的Handler處理器及Interceptor攔截器,並將它們封裝在HandlerExecutionChain對象中,返回給中央調度器。其經常使用的實現類有兩種:
一、BeanNameUrlHandlerMapping
二、SimpleUrlHandlerMappingweb
這裏着重說明SimpleUrlHandlerMapping。BeanNameUrlHandlerMapping咱們簡單來看一下源碼:spring
public class BeanNameUrlHandlerMapping extends AbstractDetectingUrlHandlerMapping { /** * Checks name and aliases of the given bean for URLs, starting with "/". */ @Override protected String[] determineUrlsForHandler(String beanName) { List<String> urls = new ArrayList<String>(); if (beanName.startsWith("/")) { urls.add(beanName); } String[] aliases = getApplicationContext().getAliases(beanName); for (String alias : aliases) { if (alias.startsWith("/")) { urls.add(alias); } } return StringUtils.toStringArray(urls); } }
上方BeanNameUrlHandlerMapping類中只有一個方法determineUrlsForHandler()。該方法中把全部以斜槓開頭的請求的BeanName都放到了一個名叫urls的List中。而後返回給了中央調度器。可是有一個弊端,好比我有兩個請求都由一個Controller來處理,這時咱們的springmvc.xml文件的配置方式以下:spring-mvc
<!-- 註冊SpringMVC處理器 --> <bean id="/my.do" class="cn.wechatbao.controller.MyController"></bean> <bean id="/you.do" class="cn.wechatbao.controller.MyController"></bean>
這樣Spring容器在建立MyController實例的時候,一次性建立了兩個,而對於咱們來講,其實一個實例就能夠對兩個或多個請求進行處理。因此咱們繼續來說解SimpleUrlHandlerMapping方法。mvc
2、處理器映射器(SimpleUrlHandlerMapping)
要使用SimpleUrlHandlerMapping咱們須要將其註冊到SpringMVC中,如圖,咱們在默認的處理器映射器中並無發現該實現類。因此須要註冊。app
註冊方式:(springmvc.xml以下)cors
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!-- 註冊視圖解析器 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/" /> <property name="suffix" value=".jsp" /> </bean> <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="mappings"> <props> <prop key="/my.do">myController</prop> <prop key="/you.do">myController</prop> <prop key="/he.do">myController</prop> </props> </property> <!-- 這種方式也能夠實現多個請求交由一個Controller處理的需求 <property name="urlMap"> <map> <entry key="/my.do" value="myController"></entry> <entry key="/you.do" value="myController"></entry> <entry key="/he.do" value="myController"></entry> </map> </property> --> </bean> <!-- 註冊SpringMVC處理器 --> <bean id="myController" class="cn.wechatbao.controller.MyController"></bean> </beans>
注意:該種配置方式只針對多個請求用一個Controller處理才使用。jsp
3、處理器映射器源碼分析
一、當客戶端發送請求到達中央調度器(DispatcherServlet)時,DispatcherServlet首先進入到doService方法在doService()方法裏,對request設計一些屬性,而後又進入到了doDispatch()方法,在doDispatch()方法裏,咱們着重來看如下代碼:ide
// Determine handler for the current request. mappedHandler = getHandler(processedRequest);
二、從doDispatch()方法又進入了一個getHandler()的方法,以下源碼分析
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { for (HandlerMapping hm : this.handlerMappings) { if (logger.isTraceEnabled()) { logger.trace( "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'"); } HandlerExecutionChain handler = hm.getHandler(request); if (handler != null) { return handler; } } return null; }
在該方法中,使用了一個forEach循環,來循環全部的處理器映射器,根據每一個處理器映射器(HandlerMapping)來獲取與之對應的處理器執行鏈(HandlerExecutionChain)。
進入hm.getHandler(request);方法來看。
三、繼續hm.getHandler(request);方法
@Override public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { Object handler = getHandlerInternal(request); if (handler == null) { handler = getDefaultHandler(); } if (handler == null) { return null; } // Bean name or resolved handler? if (handler instanceof String) { String handlerName = (String) handler; handler = getApplicationContext().getBean(handlerName); } HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request); if (CorsUtils.isCorsRequest(request)) { CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request); CorsConfiguration handlerConfig = getCorsConfiguration(handler, request); CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig); executionChain = getCorsHandlerExecutionChain(request, executionChain, config); } return executionChain; }
四、接下來又走到了HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);這個方法。咱們就不繼續深究了。有興趣的朋友能夠本身再研究下去。咱們得出的結論是通過了一系列的方法,最終返回給中央調度器一個HandlerExecutionChain對象。明白這個咱們的目的就已經達到了。