Dispatcher是springmvc前端控制器模式的實現,它提供一個集中的請求處理機制,全部的請求都將由一個單一的處理程序處理,Dispatcher負責請求的派遣,它與spring ioc完美繼承,從而能夠擁有spring的全部好處。 html
圖2.1 原理圖前端
經過原理圖咱們可知咱們在用springmvc一直在強調的,它是由兩個容器組成,即非web層容器和web層容器,通常配置在咱們的spring.xml和springmvc.xml,非web層組件通常是整個應用都共享的,如常常用到的dao層,service層等,而DispatcherServlet則初始化springmvc上下文加載的bean如Controller、HandlerMapping、HandlerAdapter等,它只加載web層的beanjava
直接上圖(實線是繼承extend、虛線是實現(implement))web
3.2 源碼解刨spring
在初始化web項目的時候,首先須要初始化servlet(HttpServlet),而servlet的初始化過程則轉交給了HttpServletBean的init方法spring-mvc
public final void init() throws ServletException { if (logger.isDebugEnabled()) { logger.debug("Initializing servlet '" + getServletName() + "'"); } // Set bean properties from init parameters.(給組件設置初始化參數) try { PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties); BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext()); bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment())); initBeanWrapper(bw); bw.setPropertyValues(pvs, true); } catch (BeansException ex) { logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex); throw ex; } // Let subclasses do whatever initialization they like.(讓子類實現擴展,由FrameworkServlet實現) initServletBean(); if (logger.isDebugEnabled()) { logger.debug("Servlet '" + getServletName() + "' configured successfully"); } }
FrameworkServlet實現HttpServletBean的initServletBean方法mvc
protected final void initServletBean() throws ServletException { getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'"); if (this.logger.isInfoEnabled()) { this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started"); } long startTime = System.currentTimeMillis(); try { //初始化上下文 this.webApplicationContext = initWebApplicationContext(); //提供給子類初始化的擴展點 initFrameworkServlet(); } catch (ServletException ex) { this.logger.error("Context initialization failed", ex); throw ex; } catch (RuntimeException ex) { this.logger.error("Context initialization failed", ex); throw ex; } if (this.logger.isInfoEnabled()) { long elapsedTime = System.currentTimeMillis() - startTime; this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " + elapsedTime + " ms"); } }
初始化上下文app
//初始化上下文,而且將之賦給servlet protected WebApplicationContext initWebApplicationContext() { //ROOT上下文(ContextLoaderListener加載的) WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null; if (this.webApplicationContext != null) { // A context instance was injected at construction time -> use it // 一、建立該Servlet注入的上下文 wac = this.webApplicationContext; if (wac instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; if (!cwac.isActive()) { // The context has not yet been refreshed -> provide services such as // setting the parent context, setting the application context id, etc if (cwac.getParent() == null) { // The context instance was injected without an explicit parent -> set // the root application context (if any; may be null) as the parent cwac.setParent(rootContext); } configureAndRefreshWebApplicationContext(cwac); } } } if (wac == null) { // No context instance was injected at construction time -> see if one // has been registered in the servlet context. If one exists, it is assumed // that the parent context (if any) has already been set and that the // user has performed any initialization such as setting the context id //二、查找已經綁定的上下文 wac = findWebApplicationContext(); } if (wac == null) { // No context instance is defined for this servlet -> create a local one //三、若是沒有找到相應的上下文,並指定父親爲ContextLoaderListener wac = createWebApplicationContext(rootContext); } if (!this.refreshEventReceived) { // Either the context is not a ConfigurableApplicationContext with refresh // support or the context injected at construction time had already been // refreshed -> trigger initial onRefresh manually here. //四、刷新上下文初始化web層組件 onRefresh(wac); } if (this.publishContext) { // Publish the context as a servlet context attribute. 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; }
在DispatcherServlet中有一處比較核心的初始化代碼:它初始化了許多核心web組件,並且咱們下面的許多篇章都會圍繞這這些組件ide
/** * Initialize the strategy objects that this servlet uses. * <p>May be overridden in subclasses in order to initialize further strategy objects. */ protected void initStrategies(ApplicationContext context) { initMultipartResolver(context); initLocaleResolver(context); initThemeResolver(context); initHandlerMappings(context); initHandlerAdapters(context); initHandlerExceptionResolvers(context); initRequestToViewNameTranslator(context); initViewResolvers(context); initFlashMapManager(context); }
用過springmvc web的人都知道,咱們項目中都會有一個web.xml文件,而須要啓動springmvc咱們須要加入如下配置ui
<listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:config/spring.xml</param-value> </context-param> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:config/spring-mvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
load-on-startup:表示容器啓動時初始化該servlet
url-pattern:表示springmvc攔截的url路徑,/表示全攔截,還有好比是*.do表示攔截.do結尾的請求
DispatcherServlet能夠配置本身的初始化參數
contextClass | 實現WebApplicationContext接口的類,當前的servlet用它來建立上下文。若是這個參數沒有指定, 默認使用XmlWebApplicationContext。 |
contextConfigLocation | 傳給上下文實例(由contextClass指定)的字符串,用來指定上下文的位置。這個字符串能夠被分紅多個字符串(使用逗號做爲分隔符) 來支持多個上下文(在多上下文的狀況下,若是同一個bean被定義兩次,後面一個優先) |
namespace | WebApplicationContext命名空間。默認值是[server-name]-servlet |
contextAttribute | ServletContext的一些屬性 |