Spring容器创建的过程,本文并不准备详细介绍,因为本系列博文的主题是分析SpringBoot源码,重点放在SpringBoot基于Spring所做的扩展
这个系列最初的目标,是把SpringBoot的源码一行一行地掰扯清楚,目前也尽力在按这个标准来写,但要说把Spring源码也逐行分析清楚,我还不敢夸下这个海口,而且就我目前掌握的皮毛,感觉没有个几十篇文章也不太好说清楚的…

所以接下来进入容器相关的流程后,对Spring源码还不太熟悉的朋友,可以先找资料研究下Spring的源码,包括后续进入容器的refresh过程,重点也会放在SpringBoot相关的扩展,比如自动配置、启动内置tomcat等等环节上,而Spring本身复杂的refresh流程,可能就一笔带过了

下面附上我之前总结的Spring容器初始化的流程图,是基于5.X版本的AnnotationConfigApplicationContext进行梳理的,希望能对各位有一点点帮助

springboot 创建json springboot 创建容器_springboot 创建json

言归正传,SpringBoot创建容器的代码,位于SpringApplication的run方法中,createApplicationContext这行代码,如下图所示

springboot 创建json springboot 创建容器_构造方法_02


查看该方法的实现

protected ConfigurableApplicationContext createApplicationContext() {
    Class<?> contextClass = this.applicationContextClass;
    if (contextClass == null) {
        try {
            switch(this.webApplicationType) {
            case SERVLET:
                contextClass = Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");
                break;
            case REACTIVE:
                contextClass = Class.forName("org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext");
                break;
            default:
                contextClass = Class.forName("org.springframework.context.annotation.AnnotationConfigApplicationContext");
            }
        } catch (ClassNotFoundException var3) {
            throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", var3);
        }
    }
    return (ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass);
}

先根据应用类型来决定创建什么类型的容器,并实例化,按前文分析,应用类型有三种,SERVLET、REACTIVE 以及 NONE,一般的springboot应用都是SERVLET类型,所以创建的容器类型为AnnotationConfigServletWebServerApplicationContext,另外通过上面代码,可以看到default分支,也就是非web环境下,创建的就是上面提到的AnnotationConfigApplicationContext,各位刚开始学习Spring源码的时候,也可以从这个容器入手

看下AnnotationConfigServletWebServerApplicationContext的构造方法

public class AnnotationConfigServletWebServerApplicationContext extends ServletWebServerApplicationContext implements AnnotationConfigRegistry {
    private final AnnotatedBeanDefinitionReader reader;
    private final ClassPathBeanDefinitionScanner scanner;
    private final Set<Class<?>> annotatedClasses;
    private String[] basePackages;

    public AnnotationConfigServletWebServerApplicationContext() {
        this.annotatedClasses = new LinkedHashSet();
        this.reader = new AnnotatedBeanDefinitionReader(this);
        this.scanner = new ClassPathBeanDefinitionScanner(this);
    }

其构造方法和AnnotationConfigApplicationContext的构造方法大体一致,都创建了一个Reader和一个Scanner,类型也是完全一样的,在创建Reader的过程中,会往Spring容器里放入一些最初的Bean,包括后续会完成扫描的ConfigurationClassPostProcessor,这些就不详细展开了

需要注意的事,在创建Reader的过程中,会初始化一个Environment,看下这个Reader的构造方法

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
        this(registry, getOrCreateEnvironment(registry));
    }

getOrCreateEnvironment方法会到容器里获取Environment,如果没有就会新建一个
这里创建的容器实现了EnvironmentCapable 接口,所以会到容器中取Environment

private static Environment getOrCreateEnvironment(BeanDefinitionRegistry registry) {
        Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
        return (Environment)(registry instanceof EnvironmentCapable ? ((EnvironmentCapable)registry).getEnvironment() : new StandardEnvironment());
    }

调用父类AbstractApplicationContext的getEnvironment方法,容器刚创建的时候environment是空的,进入if分支,调用createEnvironment方法

public ConfigurableEnvironment getEnvironment() {
        if (this.environment == null) {
            this.environment = this.createEnvironment();
        }

        return this.environment;
    }

这个方法在容器具体类型的父类GenericWebApplicationContext中有重写,新建了一个StandardServletEnvironment,这个类型的Environment我们第五篇文章开始详细介绍过了

protected ConfigurableEnvironment createEnvironment() {
        return new StandardServletEnvironment();
    }

但是这里创建的是一个新的Environment,跟我们之前SpringApplication创建的并不是同一个,它里面只包含了最初的四个PropertySource

  • servletConfigInitParams
  • servletContextInitParams
  • systemProperties
  • systemEnvironment

而我们之前介绍的Environment已经经历了配置文件装载等一系列加工,后续会用它把容器里的替换掉,但是在替换之前,SpringBoot在下面的一个环节使用了容器原生的Environement,我觉得这个是略有问题的,我们下篇文章看到了再说,这里先有个印象