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容器启动和销毁。
配置-监听ServletContext生命周期
在web.xml中spring配置了对ServletContext生命周期的监听,当Web容器启动和销毁时,触发Spring定义的IOC容器的启动和销毁,具体配置如下:
<listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener>
入口-ContextLoaderListener对Spring IOC初始化和销毁
当web容器启动时会触发contextInitialized方法对spring ioc容器进行初始化,销毁时会触发contextDestroyed方法对spring ioc容器进行销毁,ServletContextListener接口如下:
public void contextInitialized ( ServletContextEvent sce ); // ServletContext启动时触发 public void contextDestroyed ( ServletContextEvent sce ); // ServletContext销毁时触发
Spring IOC启动
spring ioc容器初始化具体代码如下:
org.springframework.web.context.ContextLoaderListener public void contextInitialized(ServletContextEvent event) { this.contextLoader = createContextLoader(); if (this.contextLoader == null) { this.contextLoader = this; } // 对spring ioc容器进行初始化 this.contextLoader.initWebApplicationContext(event.getServletContext()); }
IOC容器的初始化是由ContextLoader类执行:
org.springframework.web.context.ContextLoader public WebApplicationContext initWebApplicationContext(ServletContext servletContext) { // 是否已经加载IOC容器 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); } // 此方法是刷新初始化IOC容器的地方 configureAndRefreshWebApplicationContext(cwac, servletContext); } } // IOC容器加载后,将IOC容器保存于ServletContext容器中 servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context); ... }
熟悉的refresh()方法的调用:
org.springframework.web.context.ContextLoader protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) { if (ObjectUtils.identityToString(wac).equals(wac.getId())) { // The application context id is still set to its original default value // -> assign a more useful id based on available information String idParam = sc.getInitParameter(CONTEXT_ID_PARAM); if (idParam != null) { wac.setId(idParam); } else { // Generate default id... if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) { // Servlet <= 2.4: resort to name specified in web.xml, if any. wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(sc.getServletContextName())); } else { wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(sc.getContextPath())); } } } wac.setServletContext(sc); String initParameter = sc.getInitParameter(CONFIG_LOCATION_PARAM); if (initParameter != null) { wac.setConfigLocation(initParameter); } customizeContext(sc, wac); // 熟悉的refresh()对Spring IOC容器进行加载,接下来的步骤就是 "自我分析-Spring IOC"文脏中的内容 wac.refresh(); }
Spring IOC销毁
对Spring IOC容器和其他Spring环境信息进行销毁:
org.springframework.web.context.ContextLoaderListener public void contextDestroyed(ServletContextEvent event) { if (this.contextLoader != null) { this.contextLoader.closeWebApplicationContext(event.getServletContext()); } ContextCleanupListener.cleanupAttributes(event.getServletContext()); }
具体清理销毁了spring的什么东西,自己再跟踪下代码即可。
若文中存在分析错误,望留言指出,在此非常感谢。