Servlet容器實例化ContextLoaderListenerhtml
Servlet容器會實例化一個ContextLoaderListener前端
<listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
該類繼承ContextLoader,實現了ServletContextListener接口,使之具備listener功能java
public class ContextLoaderListener extends ContextLoader implements ServletContextListener
ContextLoaderListener 實現了ServletContextListener接口web
public interface ServletContextListener extends EventListener { /** * web應用初始化開始的時候,收到通知, 全部的ServletContextListeners都會 * 收到通知,在任何filter或者servlet以前 */ public void contextInitialized(ServletContextEvent sce); /** * ServletContext準備關閉的時候,會調用該方法; * 在contextDestroyed調用前,全部的servlet和filter都已經銷燬了 */ public void contextDestroyed(ServletContextEvent sce); }
/** * Initialize the root web application context. */ public void contextInitialized(ServletContextEvent event) { this.contextLoader = createContextLoader(); if (this.contextLoader == null) { this.contextLoader = this; } this.contextLoader.initWebApplicationContext(event.getServletContext()); } ... /** * Close the root web application context. */ public void contextDestroyed(ServletContextEvent event) { if (this.contextLoader != null) { this.contextLoader.closeWebApplicationContext(event.getServletContext()); } ContextCleanupListener.cleanupAttributes(event.getServletContext()); }
public void contextInitialized(ServletContextEvent sce);
方法中,獲取ServletContextpublic void contextDestroyed(ServletContextEvent event);
方法中,釋放資源。流程圖描述該過程spring
Servlet容器加載實例化ContextLoaderListener調用contextInitialized(ServletContextEvent sce)Endmvc
contextInitialized方法內部實現app
/** * Initialize the root web application context. */ public void contextInitialized(ServletContextEvent event) { //createContextLoader方法已經棄用,返回永遠是null this.contextLoader = createContextLoader(); if (this.contextLoader == null) { this.contextLoader = this; } //調用父類initWebApplicationContext方法,傳入ServletContext this.contextLoader.initWebApplicationContext(event.getServletContext()); } /** * Create the ContextLoader to use. Can be overridden in subclasses. * @return the new ContextLoader * @deprecated in favor of simply subclassing ContextLoaderListener itself * (which extends ContextLoader, as of Spring 3.0) */ @Deprecated protected ContextLoader createContextLoader() { return null; }
關鍵代碼:
this.contextLoader.initWebApplicationContext(event.getServletContext());
框架
獲得信息:
1. 主要初始化任務在initWebApplicationContext中實現.ide
ContextLoader學習
1. 初始化ApplicationContext — initWebApplicationContext
關鍵代碼:
1. this.context = createWebApplicationContext(servletContext);
2. servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
3. configureAndRefreshWebApplicationContext(cwac, servletContext);
獲得信息:
1. 該代碼建立了WebApplicationContext
2. 這個源代碼很長(- -)
3. 關鍵代碼在createWebApplicationContext(servletContext);
裏面
4.configureAndRefreshWebApplicationContext(cwac, servletContext);
建立以後的配置工做,這個也很重要,之後再說,這裏直說建立Context過程。
代碼
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) { 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. if (this.context == null) { this.context = createWebApplicationContext(servletContext); } if (this.context instanceof 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); } //此處也很重要 configureAndRefreshWebApplicationContext(cwac, servletContext); } } servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context); ClassLoader ccl = Thread.currentThread().getContextClassLoader(); if (ccl == ContextLoader.class.getClassLoader()) { currentContext = this.context; } 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; } }
2. 建立ApplicationContext — createWebApplicationContext
關鍵代碼:
1. Class<?> contextClass = determineContextClass(sc);
2. return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
獲得信息:
1. 根據傳入的sevletContext,返回使用WebApplicationContext接口的哪個實現類,默認是XmlWebApplicationContext
2. 使用BeanUtils實例化該類
源代碼
protected WebApplicationContext createWebApplicationContext(ServletContext sc) { Class<?> contextClass = determineContextClass(sc); if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { throw new ApplicationContextException("Custom context class [" + contextClass.getName() + "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]"); } return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); }
3. 決定使用哪一個WebApplicationContext?—determineContextClass
關鍵代碼:
1. String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
2. public static final String CONTEXT_CLASS_PARAM = "contextClass";
3. return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
獲得信息:
1. 從Servlet InitParam中獲取的contextClassName,決定到底使用WebApplicationContext接口的哪個實現類。
2. 若是你的web.xml中定義了以下片斷,會使用你本身的WebApplicationContext (默認」contextClass」),不然就使用XmlWebApplicationContext
3. 利用反射獲得Class
web.xml
<context-param> <param-name>contextClass</param-name> <param-value>Your ContextClass</param-value> </context-param>
代碼
protected Class<?> determineContextClass(ServletContext servletContext) { String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM); if (contextClassName != null) { try { return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader()); } catch (ClassNotFoundException ex) { throw new ApplicationContextException( "Failed to load custom context class [" + contextClassName + "]", ex); } } else { contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName()); try { return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader()); } catch (ClassNotFoundException ex) { throw new ApplicationContextException( "Failed to load default context class [" + contextClassName + "]", ex); } } }
總結
配置web.xml
<listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
initWebApplicationContext
createWebApplicationContext
根據ServletContext建立出WebApplicationContext完全暈了,後面的仍是畫個圖吧
Spring IOC容器經過ServletContextListener對servlet容器的生命週期監聽,從而實現了IOC的啓動和銷燬。
注意:
1.分析框架代碼時,要常使用類繼承、調用關係等快捷鍵,能夠更高效的學習,快捷鍵能夠設置成你習慣的按鍵;
2.本文重在怎麼自我分析框架代碼,因此對其中解析需本身實際跟蹤代碼實踐方可;
3.spring源代碼版本 spring-framework-3.2.1.RELEASE。
預覽
javax.servlet.ServletContext,Servlet容器接口。
javax.servlet.ServletContextListener,Servlet容器生命週期監聽接口。
org.springframework.web.context.ContextLoaderListener,Spring IOC容器生命週期監聽類。
org.springframework.web.context.ContextLoader,Spring IOC容器啓動和銷燬。
在web.xml中spring配置了對ServletContext生命週期的監聽,當Web容器啓動和銷燬時,觸發Spring定義的IOC容器的啓動和銷燬,具體配置以下:
[html] view plain copy
當web容器啓動時會觸發contextInitialized方法對spring ioc容器進行初始化,銷燬時會觸發contextDestroyed方法對spring ioc容器進行銷燬,ServletContextListener接口以下:
spring ioc容器初始化具體代碼以下:
IOC容器的初始化是由ContextLoader類執行:
熟悉的refresh()方法的調用:
對Spring IOC容器和其餘Spring環境信息進行銷燬:
具體清理銷燬了spring的什麼東西,本身再跟蹤下代碼便可。
首先,對於一個web應用,其部署在web容器中,web容器提供其一個全局的上下文環境,這個上下文就是ServletContext,其爲後面的spring IoC容器提供宿主環境;
其次,在web.xml中會提供有contextLoaderListener。在web容器啓動時,會觸發容器初始化事件,此時contextLoaderListener會監聽到這個事件,其contextInitialized方法會被調用,在這個方法中,spring會初始化一個啓動上下文,這個上下文被稱爲根上下文,即WebApplicationContext,這是一個接口類,確切的說,其實際的實現類是XmlWebApplicationContext。這個就是spring的IoC容器,其對應的Bean定義的配置由web.xml中的context-param標籤指定。在這個IoC容器初始化完畢後,spring以WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE爲屬性Key,將其存儲到ServletContext中,便於獲取;
再次,contextLoaderListener監聽器初始化完畢後,開始初始化web.xml中配置的Servlet,這個servlet能夠配置多個,以最多見的DispatcherServlet爲例,這個servlet其實是一個標準的前端控制器,用以轉發、匹配、處理每一個servlet請求。DispatcherServlet上下文在初始化的時候會創建本身的IoC上下文,用以持有spring mvc相關的bean。在創建DispatcherServlet本身的IoC上下文時,會利用WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE先從ServletContext中獲取以前的根上下文(即WebApplicationContext)做爲本身上下文的parent上下文。有了這個parent上下文以後,再初始化本身持有的上下文。這個DispatcherServlet初始化本身上下文的工做在其initStrategies方法中能夠看到,大概的工做就是初始化處理器映射、視圖解析等。這個servlet本身持有的上下文默認實現類也是mlWebApplicationContext。初始化完畢後,spring以與servlet的名字相關(此處不是簡單的以servlet名爲Key,而是經過一些轉換,具體可自行查看源碼)的屬性爲屬性Key,也將其存到ServletContext中,以便後續使用。這樣每一個servlet就持有本身的上下文,即擁有本身獨立的bean空間,同時各個servlet共享相同的bean,即根上下文(第2步中初始化的上下文)定義的那些bean。
說完了spring上下文的初始化過程,這三個上下文的關係應該就瞭解了。如仍是不太清楚,我就心有餘而力不足了,只能自行看代碼去了。