【原創】遨遊springmvc之DispatcherServlet

1.機制

Dispatcher是springmvc前端控制器模式的實現,它提供一個集中的請求處理機制,全部的請求都將由一個單一的處理程序處理,Dispatcher負責請求的派遣,它與spring ioc完美繼承,從而能夠擁有spring的全部好處。 html

2.原理圖

圖2.1 原理圖前端

經過原理圖咱們可知咱們在用springmvc一直在強調的,它是由兩個容器組成,即非web層容器和web層容器,通常配置在咱們的spring.xml和springmvc.xml,非web層組件通常是整個應用都共享的,如常常用到的dao層,service層等,而DispatcherServlet則初始化springmvc上下文加載的bean如Controller、HandlerMapping、HandlerAdapter等,它只加載web層的beanjava

3.源代碼解剖

3.1 依賴關係

直接上圖(實線是繼承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);
	}

4.配置

用過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的一些屬性

 

發現一個機智的導航😳

相關文章
相關標籤/搜索