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的一些属性 |