Spring启动并加载(源码解析)

扫码关注公众号:Java 技术驿站

发送:vip
将链接复制到本浏览器,永久解锁本站全部文章

【公众号:Java 技术驿站】 【加作者微信交流技术,拉技术群】

Spring启动并加载(源码解析)

一、材料

1. web.xml

          <context-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:config/spring.xml</param-value>
          </context-param>

          <!-- Spring监听器 -->
          <listener>
                <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
          </listener>     

2. ContextLoaderListener

    package org.springframework.web.context;

    public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
        ……
        public void contextInitialized(ServletContextEvent event) {
            this.initWebApplicationContext(event.getServletContext());
        }
        ……
    }

3.ContextLoader

    package org.springframework.web.context;

    public class ContextLoader {
        ……
        private static final Properties defaultStrategies;
        ……    
        private WebApplicationContext context;
        ……
        public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
            if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
                ……
            } else {
                ……
                try {
                    if (this.context == null) {
                        this.context = this.createWebApplicationContext(servletContext);
                    }
                    ……
                    return this.context;
                } catch (RuntimeException var8) {
                   ……
                } catch (Error var9) {
                    ……
                }
            }
        }

        ……

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

        ……

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

        ……

        protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
            String configLocationParam;
            ……
            configLocationParam = sc.getInitParameter("contextConfigLocation");
            ……
            wac.refresh();
        }

        ……

    }

二、流程

执行顺序

  1. web.xml 中配置的Spring监听器(ContextLoaderListener)被加载并调用上下文初始化方法(public void contextInitialized(ServletContextEvent event))
  2. 上下文初始化方法中调用父类(ContextLoader)方法(public WebApplicationContext initWebApplicationContext(ServletContext servletContext))去初始化Web应用上下文(this.initWebApplicationContext(event.getServletContext());)
  3. 现在我们的视野应该是在ContextLoader类中的initWebApplicationContext方法上,这个方法中忽略掉其他代码后,重点在于里面有一句this.context = this.createWebApplicationContext(servletContext);这句调用完后会返回一个被WebApplicationContext 接收的对象。
  4. 我简单介绍下WebApplicationContext 是什么。先参考文章了解ApplicationContext以及它的相关的实现子类
    WebApplicationContext 是ApplicationContext的子接口。总之是用来接收Spring上下文对象的东西。
  5. 接着,ContextLoader#createWebApplicationContext方法中做了什么事呢?它通过调用ContextLoader#determineContextClass方法确定了上下文的类并实例化该类得到对象后强转成ConfigurableWebApplicationContext返回给步骤4中的WebApplicationContext 接收。
  6. ContextLoader#determineContextClass做了什么事呢?determineContextClass这个方法我还没细看,大体上意思是如果servletContext中找不到“contextClass”对应的类名,那就走默认策略,从defaultStrategies中取,主要在这一句上contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());一般默认情况取出来的contextClassName值是“org.springframework.web.context.support.XmlWebApplicationContext”,而XmlWebApplicationContext是从web应用下的一个或多个xml配置文件加载上下文定义,适用于xml配置方式。
  7. 现在程序已经知道要以xml配置方式去加载Spring上下文,那么只剩下如何去读取到web.xml中配置好的contextConfigLocation值得到xml链接并加载了。
  8. ContextLoader#initWebApplicationContext方法中的this.context = this.createWebApplicationContext(servletContext);执行完后默认情况会得到这样的值,例如20191017100420\_1.png
    接着方法中再执行到调用this.configureAndRefreshWebApplicationContext(cwac, servletContext);这句就时用来读取到web.xml中配置好的contextConfigLocation值得到xml链接并加载了。
  9. ContextLoader#configureAndRefreshWebApplicationContext这个方法中的configLocationParam = sc.getInitParameter(“contextConfigLocation”);这句获取到了web.xml中配置好的contextConfigLocation值,也就是“classpath:config/spring-mybatis.xml”,这个方法在最后的时候调用了wac.refresh();
  10. 而这个refresh方法的实现是在XmlWebApplicationContext的父类AbstractApplicationContext里面实现的。refresh里面完成了WebApplicationContext里面的beanfactory的初始化和bean载入,beanfactorypostprocessor的调用,beanpostprocessor的注册,ApplicationEvent的监听和注册,non-lazy-init的bean的初始化。
    换言之,已经把该准备的都准备好了,只需要有请求来获取bean,就根据情况或返回已经初始化的bean或进行bean的Instantiation 和 Initialization。

参考

Spring-MVC理解之一:应用上下文webApplicationContext
spring容器和上下文的理解
spring源码分析之——Spring bean的加载入口
Sping提供了两种类型的 IOC 容器实现.
Spring基础篇——Spring容器和应用上下文理解

PS

“为避免误人子弟,以上仅供参考,个人见解,请配合多个相关博客食用……”


来源:[]()

赞(0) 打赏
版权归原创作者所有,任何形式的转载请联系博主:daming_90:Java 技术驿站 » Spring启动并加载(源码解析)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏