Spring初始化过程源码分析(1)

 2019-11-02 21:23  阅读(929)
文章分类:Spring boot

本文主要详细分析Spring初始化过程的源码分析,目的是理解Spring具体是如何工作的。部分内容查阅于网络,有不妥之处望指正。

1、web项目中服务器一启动就开始加载web.xml,Spring的启动是从web.xml中的org.springframework.web.context.ContextLoaderListener监听器(上下文加载监听器)开始的。

<context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/classes/bean-*.xml</param-value>
        </context-param>
        <listener>
            <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        </listener>

进入ContextLoaderListener类,该类中主要是上下文初始化方法contextInitialized来初始化上下文。

public void contextInitialized(ServletContextEvent event) {
            this.contextLoader = createContextLoader();
            if (this.contextLoader == null) {
                this.contextLoader = this;
            }
            this.contextLoader.initWebApplicationContext(event.getServletContext());
        }

进入initWebApplicationContext方法,主要看下面这部分

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);
                    }
                }

第二行的createWebApplicationContext(servletContext)是创建上下文的方法,进去查看该方法的源码如下:

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);
        }

进入determineContextClass(sc)方法,该方法翻译即为“确定上下文类”,也就是说这个类是能确定上下文的实现类。

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);
                }
            }
        }

首先从servletContext中查找是否有上下文类名(contextClassName )即这个servletContext.getInitParameter(CONTEXT_CLASS_PARAM),其中CONTEXT_CLASS_PARAM=“contextClass”是个常量,这个方法要做的就是从web.xml中(如下)查找param-name 为contextClass的参数值,即XmlWebApplicationContext类。

<param-name>contextClass</param-name>  
            <param-value>org.springframework.web.context.support.XmlWebApplicationContext</param-value>  
        </context-param>

如果web.xml中配置了这段就获取这个contextClassName,没有就用默认的策略contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());其中的defaultStrategies对象就是前面声明的,如下:

private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";

        private static final Properties defaultStrategies;

        static {
            // Load default strategy implementations from properties file.
            // This is currently strictly internal and not meant to be customized
            // by application developers.
            try {
                ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);
                defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
            }

其中用static代码块完成默认的处理方式。ClassPathResource 获取当前路径下的资源文件,从第一行的声明DEFAULT_STRATEGIES_PATH = “ContextLoader.properties”可以看出是叫ContextLoader.properties的文件。如下图:
20191102100765\_1.png
其中的内容为:

org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext

到此获取到了上下文实现类XmlWebApplicationContext。即 determineContextClass(sc)返回的值。下面再回到createWebApplicationContext方法,该方法获取到上下文实现类后返回return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);返回的就是实例化了这个类的对象。注意此处强制转换为ConfigurableWebApplicationContext接口,该接口是WebApplicationContext接口的子接口,XmlWebApplicationContext实现了ConfigurableWebApplicationContext接口。关系如下

20191102100765\_2.png
之后就走到了调用createWebApplicationContext方法的initWebApplicationContext方法,运行到这段代码:

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);
                    }
                }

此时会进入这个if代码段,主要看configureAndRefreshWebApplicationContext(cwac, servletContext)这个方法。这个方法第一步先设置上下文id, 再读取web.xml中配置的contextConfigLocation参数值,进一步设置环境,并加载环境初始化参数(会取web.xml中读取,如果有配置的话)

customizeContext(sc, wac); 方法前主要是自定义初始化,只需要实现接口org.springframework.context.ApplicationContextInitializer

wac.refresh(); 这里才是真正加载xml配置, 这里调用了org.springframework.context.support.AbstractApplicationContext的refresh()。

限于篇幅剩下的部分看第二篇详解。


来源:http://ddrv.cn

点赞(0)
版权归原创作者所有,任何形式转载请联系作者; Java 技术驿站 >> Spring初始化过程源码分析(1)

相关推荐