引言:
作为一名码农,虽然不会不做功课,却也从不主动撰写技术博客。先前总是觉得与其花时间写写,不如直接去再读读源码。但在这个开源的时代,闭门造车,自造轮子的方式实属不智。开源,可见的不仅仅是代码本身,还有代码背后的设计思想,实现思路。科技没有孤岛,敝帚自珍者,“丧钟”为你而鸣 ~~~
挣脱思想的束缚,然后给它插上翅膀
书归正题,言归正传......
准备工作:
1、spring 源码版本选取: spring 官网current version spring framework 4.3.7 去下载
3、一个开发用IDE. 如:Eclipse、 Myeclipse、IntelliJ IDEA 都可以
阅读的切入思路:
就像有一本很厚的书,我们首先要知道怎么打开它,然后打开就去看目录,然后根据目录结合自身需要,通读、速读、跳读、细读、略读
1、spring 容器自身的初始化流程
2、spring 源码的类层次结构
3、源码解读
阅读的切入点:
1、从 web.xml 初始化 ContextLoaderListener
<!-- spring 监听器 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
2、 ContextLoaderListener 类源码如下:package org.springframework.web.context; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import org.springframework.web.context.ContextCleanupListener; import org.springframework.web.context.ContextLoader; import org.springframework.web.context.WebApplicationContext; public class ContextLoaderListener extends ContextLoader implements ServletContextListener { public ContextLoaderListener() { } public ContextLoaderListener(WebApplicationContext context) { super(context); } public void contextInitialized(ServletContextEvent event) { this.initWebApplicationContext(event.getServletContext()); } public void contextDestroyed(ServletContextEvent event) { this.closeWebApplicationContext(event.getServletContext()); ContextCleanupListener.cleanupAttributes(event.getServletContext()); } }
分析:
一、 源码基于Servlet.(废话)
二、 继承ContextLoader, 实现 ServletContextListener
三、 无参、有参构造函数、初始化方法contextInitiallized(ServletContextEvent event) 及 销毁方法contextDestroyed(ServletContextEvent event)
四、父类 ContextLoader 提供的方法 有两个被用到,分别是: initWebApplicationContext() 和 closeWebApplicationContext()
五、ContextCleanupListener 清除 属性方法cleanupAttributes(), 清除对应的ServletContext
ContextLoader 类相关的两个方法的源码如下:
WebApplicationContext initWebApplicationContext() 方法
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) { /* * 最外层的判断是,为了防止根容器的重复启动 * String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT"; * 这是Spring容器对自己的容器级对象的自定义名称,方便获取和区分 */ 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!"); } else { 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 { if(this.context == null) { // 这里调用了ContextLoader 的 createWebApplicationContext() 创建了一个应用上下文的实例 this.context = this.createWebApplicationContext(servletContext); } if(this.context instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext err = (ConfigurableWebApplicationContext)this.context; if(!err.isActive()) { if(err.getParent() == null) { ApplicationContext elapsedTime = this.loadParentContext(servletContext); err.setParent(elapsedTime); } this.configureAndRefreshWebApplicationContext(err, servletContext); } } /* * 这里表明了容器初始化的方式是无参到有参。我们前边的ContextLoaderListener 和 ContextLoader本身都有两个构造函数 * 这一方法的前边部分都是在判断和创建Spring的WebApplicationContext 对象。这里才算创建的对象赋值 * servletContext 是 tomcat级的,Spring在这其中放入了自己定义的描述自身的实例化对象 * servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context); // 当前线程获取上下文类加载器 这里我们先不去过分细究它在这里究竟干了些什么 ClassLoader err1 = Thread.currentThread().getContextClassLoader(); if(err1 == ContextLoader.class.getClassLoader()) { currentContext = this.context; } else if(err1 != null) { currentContextPerThread.put(err1, 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 elapsedTime1 = System.currentTimeMillis() - startTime; logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime1 + " ms"); } return this.context; // 在正常启动的情况下,将返回Spring自己的WebApplicationContext对象的实例 } catch (RuntimeException var8) { logger.error("Context initialization failed", var8); servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, var8); throw var8; } catch (Error var9) { logger.error("Context initialization failed", var9); servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, var9); throw var9; } } } /** * 并附上init~方法调用的create~方法源码 */
protected WebApplicationContext createWebApplicationContext(ServletContext sc) { Class contextClass = this.determineContextClass(sc); if(!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { throw new ApplicationContextException("Custom context class [" + contextClass.getName() + "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]"); } else { return (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass); } }
/** * determineContextClass方法源码 */protected Class<?> determineContextClass(ServletContext servletContext) { String contextClassName = servletContext.getInitParameter("contextClass"); if(contextClassName != null) { try { return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader()); } catch (ClassNotFoundException var4) { throw new ApplicationContextException("Failed to load custom context class [" + contextClassName + "]", var4); } } else { contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName()); try { return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader()); } catch (ClassNotFoundException var5) { throw new ApplicationContextException("Failed to load default context class [" + contextClassName + "]", var5); } } }
void closeWebApplicationContext() 方法
public void closeWebApplicationContext(ServletContext servletContext) { servletContext.log("Closing Spring root WebApplicationContext"); boolean var6 = false; try { var6 = true; if(this.context instanceof ConfigurableWebApplicationContext) { ((ConfigurableWebApplicationContext)this.context).close(); var6 = false; } else { var6 = false; } } finally { if(var6) { ClassLoader ccl1 = Thread.currentThread().getContextClassLoader(); if(ccl1 == ContextLoader.class.getClassLoader()) { currentContext = null; } else if(ccl1 != null) { currentContextPerThread.remove(ccl1); } servletContext.removeAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE); if(this.parentContextRef != null) { this.parentContextRef.release(); } } } ClassLoader ccl = Thread.currentThread().getContextClassLoader(); if(ccl == ContextLoader.class.getClassLoader()) { currentContext = null; } else if(ccl != null) { currentContextPerThread.remove(ccl); } servletContext.removeAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE); if(this.parentContextRef != null) { this.parentContextRef.release(); } }
ContextCleanupListener 类源码如下:package org.springframework.web.context; import java.util.Enumeration; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.DisposableBean; public class ContextCleanupListener implements ServletContextListener { private static final Log logger = LogFactory.getLog(ContextCleanupListener.class); public ContextCleanupListener() { } public void contextInitialized(ServletContextEvent event) { } public void contextDestroyed(ServletContextEvent event) { cleanupAttributes(event.getServletContext()); } static void cleanupAttributes(ServletContext sc) { Enumeration attrNames = sc.getAttributeNames(); while(attrNames.hasMoreElements()) { String attrName = (String)attrNames.nextElement(); if(attrName.startsWith("org.springframework.")) { Object attrValue = sc.getAttribute(attrName); if(attrValue instanceof DisposableBean) { try { ((DisposableBean)attrValue).destroy(); } catch (Throwable var5) { logger.error("Couldn\'t invoke destroy method of attribute with name \'" + attrName + "\'", var5); } } } } } }
至此,根据web.xml 中对于Spring的配置,完成对Spring容器相应配置文件的加载。初始化的细节流程会在后续的博客中进行分析,在init~的方法中.....我们已经可以看到,在控制台初始化Servlet容器(如Tomcat)的时候,打印的熟悉的信息
"Initializing Spring root WebApplicationContext" // 正在初始化Spring 根 web 应用上下文容器 "Root WebApplicationContext: initialization started" // 初始化开始 Root WebApplicationContext: initialization completed in " + elapsedTime1 + " ms" // 启动耗时 ms
如果你有兴趣,可以试着打断点,debug一下容器的初始化过程......一步步跟随着......
Spring的神秘面纱也将慢慢被揭开~