一头扎进Spring源码之获取与切入思路


引言:

作为一名码农,虽然不会不做功课,却也从不主动撰写技术博客。先前总是觉得与其花时间写写,不如直接去再读读源码。但在这个开源的时代,闭门造车,自造轮子的方式实属不智。开源,可见的不仅仅是代码本身,还有代码背后的设计思想,实现思路。科技没有孤岛,敝帚自珍者,“丧钟”为你而鸣 ~~~

挣脱思想的束缚,然后给它插上翅膀

书归正题,言归正传......

准备工作:

1、spring 源码版本选取: spring 官网current version spring framework 4.3.7  去下载

2、spring 参考文档和API的访问地址 

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的神秘面纱也将慢慢被揭开~

                                                         


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值