SpringMVC 的啟動流程(原始碼)
阿新 • • 發佈:2019-01-14
- 說明: 我們在以往的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檔案中的標籤實現初始化,還請自行去研究
- 初次寫個人部落格,如有不正還請多多指出