SpringMVC是很是優秀的MVC框架,每個框架都是爲了咱們提升開發效率,咱們試圖經過對SpringMVC的源碼去了解這個框架,瞭解整個設計思想,框架要有擴展性,這裏用的比較可能是接口和抽象,是框架的主力,咱們經過了解源碼能對SpringMVC框架更瞭解,也能對咱們開發思想有很是大的啓示。html
SpringMVC由幾個核心類和接口組成的。咱們今天要的一個是DispatcherServlet核心的前置控制器。配置在Web.xml中。因此請求都通過它來統一分發的。SpringMVC幾個核心類和接口都會出現在DispatcherServlet的源代碼中,我這裏大概介紹一個。今天重點是介紹DispatcherServlet核心的前置控制器。後面咱們在具體分析其餘的幾個核心類和接口分析。java
DispatcherServlet的繼承關係圖,能清晰的瞭解整個層次。web
當Web項目啓動時。作初始化工做。因此咱們大部分是配置在Web.xml裏面,這樣項目一啓動,就會運行相關的初始化工做,如下是Web.xml代碼:spring
<servlet> <servlet-name>SpringMVCDispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value> classpath:spring-mvc.xml </param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>SpringMVCDispatcher</servlet-name> <url-pattern>*.jhtml</url-pattern> </servlet-mapping>
<servlet> <servlet-name>HessianDispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value> classpath:hessian-service.xml </param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>HessianDispatcher</servlet-name> <url-pattern>/service/*</url-pattern> </servlet-mapping>這裏配置了兩個 DispatcherServlet,後面會介紹到。怎麼各自處理,有各自的上下文容器。
load-on-startup是啓動的優先級,spring-mvc.xml是咱們配置bean的一些信息編程
最先咱們開始學習MVC結構時。就是學servlet,都是繼 承了HttpServlet 類,也是又一次了init、doGet、doPost、destroy方法,我這邊就不介紹HttpServlet 類,DispatcherServlet也是間接最高繼承了HttpServlet,如圖所看到的:spring-mvc
咱們先了解項目啓動,DispatcherServlet和父類都作了什麼事情呢?這是咱們今天的重點。緩存
第一步:DispatcherServlet繼承了FrameworkServlet,FrameworkServlet繼承了HttpServletBean,HttpServletBean繼承了HttpServlet 類,而HttpServletBean類有一個入口點就是重寫了init方法。如圖所看到的:mvc
init方法作了什麼事情呢?接下來咱們來詳細分析:app
init方法裏有涉及到了BeanWrapper。PropertyValues,ResourceLoader。我這裏大概介紹一下。框架
1)PropertyValues:獲取Web.xml裏面的servlet的init-param(web.xml)
/** * Create new ServletConfigPropertyValues. * @param config ServletConfig we'll use to take PropertyValues from * @param requiredProperties set of property names we need, where * we can't accept default values * @throws ServletException if any required properties are missing */ public ServletConfigPropertyValues(ServletConfig config, Set<String> requiredProperties) throws ServletException { Enumeration en = config.getInitParameterNames(); while (en.hasMoreElements()) { String property = (String) en.nextElement(); Object value = config.getInitParameter(property); addPropertyValue(new PropertyValue(property, value)); } }
說明:
Enumeration en = config.getInitParameterNames();
獲取了init-param的param-name和param-value值,並設置配置參數到PropertyValue,如圖所看到的:
2)BeanWrapper:封裝了bean的行爲,提供了設置和獲取屬性值。它有相應的BeanWrapperImpl。如圖所看到的:
3)ResourceLoader:接口僅有一個getResource(String location)的方法。可以依據一個資源地址載入文件資源。classpath:這樣的方式指定SpringMVC框架bean配置文件的來源。
ResourcePatternResolver擴展了ResourceLoader接口。獲取資源
ResourcePatternResolver resolver =new PathMatchingResourcePatternResolver();
resolver.getResources("classpath:spring-mvc.xml");
總結:
先經過PropertyValues獲取web.xml文件init-param的參數值,而後經過ResourceLoader讀取.xml配置信息,BeanWrapper對配置的標籤進行解析和將系統默認的bean的各類屬性設置到相應的bean屬性。
在init方法裏還調用了initServletBean();這裏面又實現了什麼。HttpServletBean在爲子類提供模版、讓子類依據本身的需求實現不一樣的ServletBean的初始化工做。這邊是由HttpServletBean的子類FrameworkServlet來實現的,如圖所看到的:
this.webApplicationContext = initWebApplicationContext();初始化SpringMVC 上下文容器。servlet的上下文容器是ServletContext。對initWebApplicationContext()。進行跟蹤,查看這種方法作了什麼事情?
protected WebApplicationContext initWebApplicationContext() { //根節點上下文,是經過ContextLoaderListener載入的,server啓動時,最早載入的 WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); if (this.webApplicationContext != null) { wac = this.webApplicationContext; if (wac instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; //要對上下文設置父上下文和ID等 if (!cwac.isActive()) { if (cwac.getParent() == null) { cwac.setParent(rootContext); } configureAndRefreshWebApplicationContext(cwac); } } } //Servlet不是由編程式註冊到容器中,查找servletContext中已經註冊的WebApplicationContext做爲上下文 if (wac == null) { wac = findWebApplicationContext(); } //假設都沒找到時,就用根上下文就建立一個上下文有ID if (wac == null) { wac = createWebApplicationContext(rootContext); } //在上下文關閉的狀況下調用refesh可啓動應用上下文,在已經啓動的狀態下。調用refresh則清除緩存並又一次裝載配置信息 if (!this.refreshEventReceived) { onRefresh(wac); } //對不一樣的請求相應的DispatherServlet有不一樣的WebApplicationContext、並且都存放在ServletContext中 if (this.publishContext) { String attrName = getServletContextAttributeName(); getServletContext().setAttribute(attrName, wac); if (this.logger.isDebugEnabled()) { this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() + "' as ServletContext attribute with name [" + attrName + "]"); } } return wac; }
總結:
initWebApplicationContext初始化上下文。並做爲值放到了ServletContext裏,因爲不一樣的DispatherServlet有相應的各自的上下文,而且上下文有設置父上下文和id屬性等。上下文項目啓動時會調用createWebApplicationContext()方法,如圖所看到的:
而後會初始化,設置設置父上下文和id屬性等。如圖所看到的:
1)獲取ContextLoaderListener載入的上下文並標示爲跟上下文,假設是編程式傳入。沒初始化,以根節點爲父上文。並設置ID等信息。而後初始化。
2)假設上下文是爲空的,Servlet不是由編程式註冊到容器中,查找servletContext中已經註冊的WebApplicationContext做爲上下文,假設都沒找到時,就用根上下文就建立一個上下文有ID。在上下文關閉的狀況下調用refesh可啓動應用上下文,在已經啓動的狀態下,調用refresh則清除緩存並又一次裝載配置信息
3)對不一樣的請求相應的DispatherServlet有不一樣的WebApplicationContext、並且都存放在ServletContext中。以servlet-name爲key保存在severtContext。前面有配置了兩個DispatherServlet,都有各自的上下文容器。如圖所看到的:
回調函數onRefresh還作了一些提供了SpringMVC各類編程元素的初始化工做。 onRefresh在爲子類提供模版、讓子類依據本身的需求實現不一樣的onRefresh的初始化工做。這邊是由FrameworkServlet的子類DispatcherServlet來實現的,如圖所看到的:
咱們現在來分析SpringMVC組件進行初始化。並封裝到DispatcherServlet中
//初始化上傳文件解析器
initMultipartResolver(context);
//初始化本地解析器
initLocaleResolver(context);
//初始化主題解析器
initThemeResolver(context);
//初始化映射處理器
initHandlerMappings(context);
//初始化適配器處理器
initHandlerAdapters(context);
//初始化異常處理器
initHandlerExceptionResolvers(context);
//初始化請求到視圖名翻譯器
initRequestToViewNameTranslator(context);
//初始化視圖解析器
initViewResolvers(context);
咱們這邊拿幾個比較基本的分析一下詳細實現了什麼。
第一:initHandlerMappings初始化映射處理器
private void initHandlerMappings(ApplicationContext context) { this.handlerMappings = null; if (this.detectAllHandlerMappings) { Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false); if (!matchingBeans.isEmpty()) { this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values()); // We keep HandlerMappings in sorted order. OrderComparator.sort(this.handlerMappings); } } else { try { HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class); this.handlerMappings = Collections.singletonList(hm); } catch (NoSuchBeanDefinitionException ex) { } } if (this.handlerMappings == null) { this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class); if (logger.isDebugEnabled()) { logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default"); } } }
說明:
1)detectAllHandlerMappings默認是true,依據類型匹配機制查找上下文及父容器上下文中所有類型爲HandlerMapping的bean,將它們做爲該類型組件。並放到ArrayList<HandlerMapping>中。
2)detectAllHandlerMappings假設是false時。查找key爲handlerMapping的HandlerMapping類型的bean爲該類組件,而且 Collections.singletonList僅僅有一個元素的集合。
3)List<HandlerMapping> 是爲空的話,使用BeanNameUrlHandleMapping實現類建立該類的組件。
第二:initHandlerAdapters適配器處理器
private void initHandlerAdapters(ApplicationContext context) { this.handlerAdapters = null; if (this.detectAllHandlerAdapters) { Map<String, HandlerAdapter> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false); if (!matchingBeans.isEmpty()) { this.handlerAdapters = new ArrayList<HandlerAdapter>(matchingBeans.values()); // We keep HandlerAdapters in sorted order. OrderComparator.sort(this.handlerAdapters); } } else { try { HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class); this.handlerAdapters = Collections.singletonList(ha); } catch (NoSuchBeanDefinitionException ex) { } } if (this.handlerAdapters == null) { this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class); if (logger.isDebugEnabled()) { logger.debug("No HandlerAdapters found in servlet '" + getServletName() + "': using default"); } } }
initHandlerAdapters適配器處理器初始化原理跟initHandlerMappings初始化映射處理器同樣
這裏就不在說明了。
總結:
1)initHandlerMapping會初始化了handlerMethods請求方法的映射。HandlerMapping是處理請求的映射的如圖所看到的:
今天先講SpringMVC的初始化。當DispatcherServlet初始化後,就會本身主動掃描上下文的bean,依據名稱或者類型匹配的機制查找本身定義的組件,找不到則使用DispatcherServlet。Properties定義默認的組件
總結:
HttpServletBean、FrameworkServlet、DispatcherServlet三個不一樣的類層次,SpringMVC對三個以抽象和繼承來實現不用的功能。分工合做。實現瞭解耦的設計原則。
咱們在回想一下。各自作了什麼事情。HttpServletBean是實現了獲取web.xml中的<init-param>配置元素的值。FrameworkServlet實現了SpringMVC上下文並依據不一樣的DispatcherServlet放在以servlet-name爲key值severtContext中。DispatcherServlet主要實現了初始化SpringMVC組件元素。