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上下文的初始化过程,这三个上下文的关系应该就了解了。如仍是不太清楚,我就心有余而力不足了,只能自行看代码去了。