1. 程式人生 > >SpringMVC 的啟動流程(原始碼)

SpringMVC 的啟動流程(原始碼)

  • 說明: 我們在以往的web專案裡,在啟動容器的話都要在web.xml配置檔案中配置一個監聽器

例:

	<!-- 指定以Listener方式啟動Spring容器 -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
  • 這個web監聽器就是容器的初始化的入口,首先看一下這個監聽器的繼承結構
    在這裡插入圖片描述

  • ContextLoader類負責執行根應用程式上下文的實際初始化工作,ServletContextListener負責監聽web容器的初始化與銷燬

  • 整個Spring mvc 容器的啟動就是在contextInitialized()方法開始的,當整個web容器啟動時,web容器就會呼叫該方法,這也是典型的觀察者模式的應用

public class ContextLoaderListener extends ContextLoader implements ServletContextListener {

	
	public ContextLoaderListener() {	}

	public ContextLoaderListener(WebApplicationContext context) {
		super(context);
	}
	
   @Override
	public void contextInitialized(ServletContextEvent event) {
		initWebApplicationContext(event.getServletContext());
	}
  • 接下來跟蹤initWebApplicationContext(event.getServletContext())};
    event.getServletContext()可以拿到seevlet的上下文物件,當做引數傳入方法中,那麼接下來我們看看核心方法中的程式碼
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
		// 獲取上下文域中屬性,以String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT"為key去取;如果已經存在WebApplicationContext物件則報錯
		if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
			throw new IllegalStateException(
					"Cannot initialize context because there is already a root application context present - " +
					"check whether you have multiple ContextLoader* definitions in your web.xml!");
		}

		Log logger = LogFactory.getLog(ContextLoader.class);
		servletContext.log("Initializing Spring root WebApplicationContext");
		if (logger.isInfoEnabled()) {
			logger.info("Root WebApplicationContext: initialization started");
		}
		long startTime = System.currentTimeMillis();

		try {
			// Store context in local instance variable, to guarantee that
			// it is available on ServletContext shutdown.
			// 如果WebApplicationContext為空則建立一個WebApplicationContext容器,方法裡面有預設的選擇容器的策略
			// 預設容器存放在ContextLoader.properties檔案中為XmlWebApplicationContext
			if (this.context == null) {
				this.context = createWebApplicationContext(servletContext);
			}
			// XmlWebApplicationContext 是ConfigurableWebApplicationContext的實現類
			if (this.context instanceof ConfigurableWebApplicationContext) {
			   // 向上轉型為ConfigurableWebApplicationContext
				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
				// 確定此應用程式上下文是否活動,即,*是否已重新整理至少一次且尚未關閉。
				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 ->
						// determine parent for root web application context, if any.
						// 這裡面還是取拿取父容器
						ApplicationContext parent = loadParentContext(servletContext);
						cwac.setParent(parent);
					}
					// 重新整理web容器
					configureAndRefreshWebApplicationContext(cwac, servletContext);
				}
			}
			// 重新整理容器以後,放入servletContext域中
			servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
			// 判斷當前執行緒所攜帶的類載入器,與載入ContextLoader類的類載入器是否一致,如果一致則存放到
			// ClassLoader 的靜態的當前容器變數中
			ClassLoader ccl = Thread.currentThread().getContextClassLoader();
			if (ccl == ContextLoader.class.getClassLoader()) {
				currentContext = this.context;
			}
			// 如果不一致,則存放在private static final Map<ClassLoader, WebApplicationContext>   currentContextPerThread =
			new ConcurrentHashMap<ClassLoader, WebApplicationContext>(1);中
			else if (ccl != null) {
				currentContextPerThread.put(ccl, this.context);
			}

			if (logger.isDebugEnabled()) {
				logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +
						WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
			}
			if (logger.isInfoEnabled()) {
				long elapsedTime = System.currentTimeMillis() - startTime;
				logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
			}

			return this.context;
		}
		catch (RuntimeException ex) {
			logger.error("Context initialization failed", ex);
			servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
			throw ex;
		}
		catch (Error err) {
			logger.error("Context initialization failed", err);
			servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);
			throw err;
		}
	}
  • 目前我們對spring mvc 整個容器的初始化有了一個初步的概念,如果需要知道更多細節,比如怎麼取載入我們的application.xml檔案中的標籤實現初始化,還請自行去研究
  • 初次寫個人部落格,如有不正還請多多指出