run方法的整体流程

* 提供了启动一个Spring应用的流程,例如一个java web servlet的应用就会启动一个AnnotationConfigServletWebServerApplicationContext

SpringApplication.run(DemoApplication.class, args);

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
   return new SpringApplication(primarySources).run(args);
}

其中核心方法run方法实现了这个启动的流程,如下

* 实例SpringApplication类,构造方法内做一些初始处理,接着执行run方法

①resourceLoader、primarySources接受传递的参数

②确定webApplicationType(通常为Servlet)

  如果存在org.springframework.web.reactive.DispatcherHandler且不存在org.springframework.web.servlet.DispatcherServlet,则为REACTIVE

  其余情况默认为SERVLET

③获取META-INF/spring.factories指定类型配置的所有对象,

会实例化收集Bootstrapper、ApplicationContextInitializer、ApplicationListener接口的所有配置的实现类对象,这里是直接new相关对象

④mainApplicationClass(根据运行栈找出运行main的类)

  从new RuntimeException().getStackTrace()找到main方法,获取所属的类

* 创建一个DefaultBootstrapContext对象—createBootstrapContext

  - 提供指定类型对象的存储Map<Class<?>, InstanceSupplier<?>> instanceSuppliers,Map<Class<?>, Object> instances,类似一个对象容器

  - 提供一个监听处理器ApplicationEventMulticaster events,以处理BootstrapContextClosedEvent事件类型的监听器的设置和触发

* 设置当前JDK支持AWT图形功能 configureHeadlessProperty();

* 获取当前项目所有META-INF/spring.factories的配置信息下SpringApplicationRunListener接口指定的使用实现类的实例(注意和容器ApplicationListener的区分)

   - SpringApplicationRunListeners listeners = getRunListeners(args);

   - 有一个默认的实现时EventPublishingRunListener,它本身也是一个事件发布器,他会收集SpringAppilcation对象中List<ApplicationListener<?>> listeners

     对他们发布对应的事件

* 调用所有SpringApplicationRunListeners.starting(bootstrapContext)

* 把args封装为ApplicationArguments

- ApplicationArguments 内部包含了arg和source;arg是原本的参数,

- source是SimpleCommandLinePropertySource,是PropertySource类型的实现类

 会处理了参数中命令行格式的,解析为key-value格式且提供可选的区分,比如--foo=bar,以‘--’开头的即为可选的参数

* 环境StandardServletEnvironment的准备-prepareEnvironment(见下)

①创建ConfigurableEnvironment,servlet环境下对应StandardServletEnvironment

②configureEnvironment进行配置和添加PropertySource

  属性加入顺序 命令行参数 ->  SpringApplication类的Map<String, Object> defaultProperties  ->  执行configureProfiles方法供子类模板

- 当addConversionService为true时,为environment.setConversionService(ApplicationConversionService对象)

    - 当SpringApplication类的Map<String, Object> defaultProperties配置内容不为空时,为key为defaultProperties,值为DefaultPropertiesPropertySource后addLast到environment

      用户可以直接调用SpringApplication类的addDefaultProperties(..)方法添加进来

    - 当addCommandLineProperties为true且args不为空时,把命令行参数封装为SimpleCommandLinePropertySource后addFirst到environment

    - 调用configureProfiles方法供子类模板实现

      这里用户可以设置一些自定义的profile,设置到环境对象中environment.setActiveProfiles

③把当前environment下的MutablePropertySources的名称转为spring配置key的标准格式,

- 如my.server_name.PORT会转为 my.servername.port再组装成一个大的PropertySource集合,name为configurationProperties,

类型为ConfigurationPropertySourcesPropertySource,这个大的PropertySource集合会addFirst到环境中

    - 此步的目的是为了把Binder.get(environment).bind("属性key", 对象);这样可以把属性值合适的绑定到一个对象中

④触发所有(SpringApplicationRunListener接口)的listeners.environmentPrepared(DefaultBootstrapContext,environment)

  - 这一步通常是加载配置文件或者配置中心的文件,解析属性添加到Environment中

    - 例如在这里会实现获取spring.profiles.active并加载类路径下application-xx.propertise/yml文件(Spring.config.name没有指定时,默认为application)

      addAfter到Environment环境中

⑤把Environment中为Name为defaultProperties的PropertySource移到最后

⑥把additionalProfiles属性匹配的有效文件添加到environment.setActiveProfiles

⑦把配置文件中spring.main.xxx的信息绑定到当前对象中,基于Binder

⑧如有必要把environment转为正确的类型,如StandardServletEnvironment

* 把environment中name为spring.beaninfo.ignore的值设置到System.setProperty

* Banner打印

* 创建应用上下文createApplicationContext(),这里默认是 AnnotationConfigServletWebServerApplicationContext:支持以配置文件启动的容器+Tomcat

  - AnnotationConfigServletWebServerApplicationContext构造方法会创建AnnotatedBeanDefinitionReader,

- AnnotatedBeanDefinitionReader的其构造方法中有调用AnnotationConfigUtils.registerAnnotationConfigProcessors

 会在bean工厂中注册必要的后置处理器,如解析@Configuration,@Autowrite的后置处理类:

ConfigurationClassPostProcessor、AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor等

* 应用上下文StandardServletEnvironment的准备prepareContext

   ①为底层BeanFactory注册beanNameGenerator单例、设置ConversionService、设置allowBeanDefinitionOverriding

    ②为Context设置environment、ResourceLoader

    ③启动收集(有SPI收集)的所有ApplicationContextInitializer初始化器、调用其initialize

    ④调用所有SpringApplicationRunListeners.starting(bootstrapContext)、bootstrapContext.close(context);

⑤对底层bean工厂进行一些设置,ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();

- 设置beanFactory.setAllowBeanDefinitionOverriding(SpringApplication指定的allowBeanDefinitionOverriding);

- 根据lazyInitialization配置,增加一个bean后置处理器LazyInitializationBeanFactoryPostProcessor等

⑥加载primarySources(run方法参数指定)和sources保存的类文件

  - 使用 BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);的load方法

  - 通常我们指定的配置类,是一个Class,此时就会判断类上是否有Component注解,是的话使用AnnotatedBeanDefinitionReader.register(source)

根据@Lazy、Primary等信息就会把该Class类对象封装为对应的BeanDefinition,注册进去工厂的定义表

    ⑦触发监听器SpringApplicationRunListeners.contextLoaded(context);

* 应用上下文的刷新refreshContext

- 根据registerShutdownHook标志(默认true)调用context.registerShutdownHook(),钩子函数的内容是会调用AbstractApplicationContext的doClose();

  - 再调用context.refresh进行容器的初始化和web服务器的启动(整体流程见下)

注:我们在之前步骤穿件AnnotationConfigServletWebServerApplicationContext的时候是没有指定参数的,所以其重写的模板方法postProcessBeanFactory内容没有生效

run方法指定配置类的注册以及在之前的步骤就以及完成。

  - Context的刷新中,由于当前类通常添加了SpringBootApplication注解,且当前类添加例如@Configuration注解,那么在refresh的invokeBeanFactoryPostProcessors阶段,

就会收集并触发工厂后置处理器ConfigurationClassPostProcessor,就会开始解析本 SpringApplication上的注解,可以走自动配置了

ConfigurationClassPostProcessor(解析@Configuration)

* 调用子类模板方法afterRefresh

* SpringApplicationRunListeners.started(context);

* Runner处理callRunners

从容器中获取ApplicationRunner、CommandLineRunner调用其run方法


public class SpringApplicationxx {
   //主配置文件资源,run方法的参数为存于此
   private Set<Class<?>> primarySources;
    //自定义新增的配置文件资源
   private Set<String> sources = new LinkedHashSet<>();
    //main方法的类
   private Class<?> mainApplicationClass;
   //是否添加命令行属性
   private boolean addCommandLineProperties = true;
    //是否自定义配置ConversionService
   private boolean addConversionService = true;
    //资源加载器
   private ResourceLoader resourceLoader;
    //beanName生成器
   private BeanNameGenerator beanNameGenerator;
    //环境对象
   private ConfigurableEnvironment environment;
    //web应用的类型,通常为servlet
   private WebApplicationType webApplicationType;
    //是否注册JVM关闭的钩子
   private boolean registerShutdownHook = true;
    //Context对象的自定义初始化器集合
   private List<ApplicationContextInitializer<?>> initializers;
    //ApplicationListener类型的监听器集合
   private List<ApplicationListener<?>> listeners;
    //默认存储的属性Map
   private Map<String, Object> defaultProperties;
    //引导配置的处理类,可存储一些对象和BootstrapContext的监听器
   private List<Bootstrapper> bootstrappers;
    //自定义的配置文件
   private Set<String> additionalProfiles = Collections.emptySet();
    //是否允许bean定义被覆盖
   private boolean allowBeanDefinitionOverriding;
    //是否是自定义的CustomEnvironment
   private boolean isCustomEnvironment = false;
    //懒初始化开关
   private boolean lazyInitialization = false;
    //返回环境符合的Context应用上下文,如java web sevlet通常为AnnotationConfigServletWebServerApplicationContext
   private ApplicationContextFactory applicationContextFactory = ApplicationContextFactory.DEFAULT;

   //指定配置类,创建SpringApplication
   public SpringApplicationxx(Class<?>... primarySources) {
      this(null, primarySources);
   }
   public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
      this.resourceLoader = resourceLoader;
      this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
      //通常值为WebApplicationType.SERVLET,表示是基于java servlet的服务器
      this.webApplicationType = WebApplicationType.deduceFromClasspath();
      //获取META-INF/spring.factories指定类型配置的所有对象,如下获取BootstrapperApplicationContextInitializerApplicationListener接口的所有配置的实现类对象
      //bootstrappers 这里是直接new了,与容器无关

//基于SPI的获取Bootstrapper、ApplicationContextInitializer、ApplicationListener类型的实例
      this.bootstrappers = new ArrayList<>(getSpringFactoriesInstances(Bootstrapper.class));
      setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
      setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
      //获取运行栈,找出执行方法为main的类型,即为mainApplicationClass
      //获取运行栈方法 StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
      this.mainApplicationClass = deduceMainApplicationClass();
   }


   public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
      return run(new Class<?>[] { primarySource }, args);
   }
   public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
      return new SpringApplicationxx(primarySources).run(args);
   }
   //核心方法,启动一个完整的应用
   public ConfigurableApplicationContext run(String... args) {
      // 创建一个DefaultBootstrapContext
      //     提供指定类型对象的存储Map<Class<?>, InstanceSupplier<?>> instanceSuppliersMap<Class<?>, Object> instances,类似一个对象容器
      //    提供一个监听处理器ApplicationEventMulticaster events,以处理BootstrapContextClosedEvent事件类型的监听器的设置和触发
      DefaultBootstrapContext bootstrapContext = createBootstrapContext();
      ConfigurableApplicationContext context = null;
      //设置当前JDK支持AWT图形功能
      configureHeadlessProperty();
      //获取当前项目所有META-INF/spring.factories的配置信息下SpringApplicationRunListener接口指定的使用实现类的实例
      SpringApplicationRunListeners listeners = getRunListeners(args);
      //调用所有SpringApplicationRunListeners.starting(bootstrapContext)
      listeners.starting(bootstrapContext, this.mainApplicationClass);
      try {
         //会处理了参数中命令行格式的,解析为key-value格式且提供可选的区分,比如 --foo=bar
         ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
         //环境StandardServletEnvironment的准备,包括Environment的创建,把命令行,配置文件登录信息设置到Environment
         ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
         //environmentnamespring.beaninfo.ignore的值设置到System.setProperty
         configureIgnoreBeanInfo(environment);
         Banner printedBanner = printBanner(environment);
         //创建用于的上下文,这里默认是 AnnotationConfigServletWebServerApplicationContext:支持以配置文件启动的容器+Tomcat
         context = createApplicationContext();
         context.setApplicationStartup(this.applicationStartup);
         //应用上下文StandardServletEnvironment的准备
         prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
         //根据registerShutdownHook调用context.registerShutdownHook();
         //再调用context.refresh进行容器的初始化和web服务器的启动
         refreshContext(context);
         //模板方法
         afterRefresh(context, applicationArguments);
         listeners.started(context);
         //从容器中获取ApplicationRunnerCommandLineRunner调用其run方法
         callRunners(context, applicationArguments);
      }
      catch (Throwable ex) {
         handleRunFailure(context, ex, listeners);
         throw new IllegalStateException(ex);
      }

      try {
         listeners.running(context);
      }
      catch (Throwable ex) {
         handleRunFailure(context, ex, null);
         throw new IllegalStateException(ex);
      }
      return context;
   }


   private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
         DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
      //创建ConfigurableEnvironmentservlet环境下对应StandardServletEnvironment
      ConfigurableEnvironment environment = getOrCreateEnvironment();
      //此类的addConversionServicetrue时,为environment.setConversionServiceApplicationConversionService对象)
      //此类的Map<String, Object> defaultProperties不为空时,转为DefaultPropertiesPropertySourceaddLastenvironment
      //此类的addCommandLineProperties为且args不为空时,把命令行参数封装为SimpleCommandLinePropertySourceaddFirstenvironment
      //configureProfiles(ConfigurableEnvironment environment, String[] args) 方法供子类实现
      configureEnvironment(environment, applicationArguments.getSourceArgs());
      //把当前environment下的MutablePropertySources的名称转为spring配置key的标准格式,如my.server_name.PORT会转为 my.servername.port
      //再组装成一个大的PropertySource集合,那么nameconfigurationProperties,类型为ConfigurationPropertySourcesPropertySource
      //sources.addFirst(new ConfigurationPropertySourcesPropertySource(ATTACHED_PROPERTY_SOURCE_NAME,new SpringConfigurationPropertySources(sources)));
      //此步的目的是为了把Binder.get(environment).bind("属性key", 对象);这样可以把属性值合适的绑定到一个对象中
      ConfigurationPropertySources.attach(environment);
      //触发(SpringApplicationRunListener接口)的listeners.environmentPrepared,例如EventPublishingRunListener
      //其中EventPublishingRunListener(在创建的时候,会把当前SpringApplicationlisteners所有监听器注册进来)environmentPrepared会发布ApplicationEnvironmentPreparedEvent事件
      //EnvironmentPostProcessorApplicationListener监听了ApplicationEnvironmentPreparedEvent事件会,会把META-INF/spring.factories下的EnvironmentPostProcessorApplicationListener对象执行其postProcessEnvironment方法
      //其中ConfigFileApplicationListener对象的postProcessEnvironment对象就是加载类路径下application.propertise/yml文件
      listeners.environmentPrepared(bootstrapContext, environment);
      //把那么为defaultPropertiesPropertySource移到最后
      DefaultPropertiesPropertySource.moveToEnd(environment);
      //additionalProfiles属性匹配的有效文件添加到environment.setActiveProfiles
      configureAdditionalProfiles(environment);
      //把配置文件中spring.main.xxx的信息绑定到当前对象中
      bindToSpringApplication(environment);
      //environment转为正确的类型,如StandardServletEnvironment
      if (!this.isCustomEnvironment) {
         environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
               deduceEnvironmentClass());
      }
      ConfigurationPropertySources.attach(environment);
      return environment;
   }


   private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
         ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
         ApplicationArguments applicationArguments, Banner printedBanner) {
      // 底层BeanFactory注册beanNameGenerator单例、设置ConversionService
      // 设置environmentResourceLoader
      context.setEnvironment(environment);
      postProcessApplicationContext(context);
      //启动initializers表收集的所有初始化器的、调用其initialize
      applyInitializers(context);
      //调用所有SpringApplicationRunListeners.starting(bootstrapContext)
      listeners.contextPrepared(context);
      bootstrapContext.close(context);
      // 底层BeanFactory设置allowBeanDefinitionOverriding
      ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
      beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
      if (printedBanner != null) {
         beanFactory.registerSingleton("springBootBanner", printedBanner);
      }
      if (beanFactory instanceof DefaultListableBeanFactory) {
         ((DefaultListableBeanFactory) beanFactory)
               .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
      }
      //根据lazyInitialization配置,增加一个bean后置处理器LazyInitializationBeanFactoryPostProcessor
      if (this.lazyInitialization) {
         context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
      }
      // 加载primarySourcesrun方法参数指定)和sources遍历保存的类文件
      // 例如加载指定的Class类对象,就会把Class类对象封装为对应的BeanDefinition,注册进去工厂的定义表
      Set<Object> sources = getAllSources();
      Assert.notEmpty(sources, "Sources must not be empty");
      load(context, sources.toArray(new Object[0]));
      listeners.contextLoaded(context);
   }



   private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
      return getSpringFactoriesInstances(type, new Class<?>[] {});
   }

   private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
      ClassLoader classLoader = getClassLoader();
      //获取当前项目所有META-INF/spring.factories的配置信息,找出指定接口名称锁配置的所有实现类名称集合
      Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
      //反射获取names的所有对象,parameterTypes对应得构造器的参数类型
      List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
      //基于@OrderPriority注解的排序
      AnnotationAwareOrderComparator.sort(instances);
      return instances;
   }

   @SuppressWarnings("unchecked")
   private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
         ClassLoader classLoader, Object[] args, Set<String> names) {
      List<T> instances = new ArrayList<>(names.size());
      for (String name : names) {
         try {
            Class<?> instanceClass = ClassUtils.forName(name, classLoader);
            Assert.isAssignable(type, instanceClass);
            Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
            T instance = (T) BeanUtils.instantiateClass(constructor, args);
            instances.add(instance);
         }
         catch (Throwable ex) {
            throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
         }
      }
      return instances;
   }

    //settergetter


}

prepareEnvironment

概述

* 调用时机

 在run方法加载META-INF/spring.factories下SpringApplicationRunListener类型的监听器之后就开始进行环境的准备了

public ConfigurableApplicationContext run(String... args) {
    //...
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    //加载监听器
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting();
    try {
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        //进行environment环境变量的准备
        ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
        configureIgnoreBeanInfo(environment);
        //....


    }

}

prepareEnvironment主要流程

* 创建ConfigurableEnvironment,servlet环境下对应StandardServletEnvironment

* configureEnvironment配置环境

属性加入顺序 命令行参数 ->  SpringApplication类的Map<String, Object> defaultProperties  ->  执行configureProfiles方法供子类模板

①当addConversionService为true时,为environment.setConversionService(ApplicationConversionService对象)

②当SpringApplication类的Map<String, Object> defaultProperties不为空时,转为DefaultPropertiesPropertySource后addLast到environment

③当SpringApplication类的addCommandLineProperties为且args不为空时,把命令行参数封装为SimpleCommandLinePropertySource后addFirst到environment

④调用configureProfiles方法供子类模板实现

* ConfigurationPropertySources.attach(environment),把当前environment下的MutablePropertySources的名称转为spring配置key的标准格式,

如my.server_name.PORT会转为 my.servername.port再组装成一个大的PropertySource集合,name为configurationProperties,

类型为ConfigurationPropertySourcesPropertySource

此步的目的是为了把Binder.get(environment).bind("属性key", 对象);这样可以把属性值合适的绑定到一个对象中

* 触发(SpringApplicationRunListener接口)的listeners.environmentPrepared(DefaultBootstrapContext,environment),这里也会加入一些属性

有多种类型的listeners,例如EventPublishingRunListener(在创建的时候,会把当前SpringApplication的listeners(ApplicationListener类型)所有监听器注册进来)

* 为Name为defaultProperties的PropertySource移到最后

* 设置environment.setActiveProfiles(SpringApplication类的additionalProfiles);

* 把配置文件中spring.main.xxx的信息绑定到当前对象中,基于Binder

* 如有必要把environment转为正确的类型,如StandardServletEnvironment

   private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
         DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
      ConfigurableEnvironment environment = getOrCreateEnvironment();
      configureEnvironment(environment, applicationArguments.getSourceArgs());
      ConfigurationPropertySources.attach(environment);
      listeners.environmentPrepared(bootstrapContext, environment);
      DefaultPropertiesPropertySource.moveToEnd(environment);
      configureAdditionalProfiles(environment);
      bindToSpringApplication(environment);
      if (!this.isCustomEnvironment) {
         environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
               deduceEnvironmentClass());
      }
      ConfigurationPropertySources.attach(environment);
      return environment;
   }

listeners.environmentPrepared

*SpringApplication构造方法把ApplicationListener类型SPI注册进来,

- 在run.prepareEnvironment方法里面会调用SpringApplicationRunListener类型的environmentPrepared方法,

这里实现类EventPublishingRunListener会调用environmentPrepared会发布ApplicationEnvironmentPreparedEvent事件

EventPublishingRunListener会遍历说明ApplicationListener类型的实现类,

* 我们关注两个监听器

  - BoostrapApplicationListener,优先级是Ordered.HIGHEST_PRECEDENCE + 5,用于处理BootstrapConfiguration配置

   会创建另一个Context以处理BootstrapConfiguration类型的配置,会获取其他的属性,最后加入原上下文的环境中

- ConfigFileApplicationListener,其优先级Ordered.HIGHEST_PRECEDENCE + 10,是用于解析application.yam/propertis文件

 

BoostrapApplicationListener处理BootstrapConfiguration配置

目的是创建一个新的Context上下文获取BootstrapConfiguration SPI指定的自动配置内容,再把此父上下文的内容加入到原Context中

从父容器中的找到ApplicationContextInitializer类型的bean,设置进去子容器中application.setInitializers(target);让子容器去执行

* BoostrapApplicationListener是spring Cluld2.7以上支持的,位于spring-cloud-context包下

  优先级是Ordered.HIGHEST_PRECEDENCE + 5

* BoostrapApplicationListener监听ApplicationEnvironmentPreparedEvent事件

  如果传递中environment存在BOOTSTRAP_PROPERTY_SOURCE_NAME,说明已经处理,结束,否则如下处理

* 一般会创建一个新的SpringApplication,做如下特殊配置,最后生成一个新的上下文

①从子环境中获取如下内容

  - 配置文件名spring.cloud.bootstrap.name,默认为bootstrap

  - 配置文件路径spring.cloud.bootstrap.location,spring.cloud.bootstrap.additional-location等信息

②创建新boostrap上下文的的环境, ConfigurableEnvironment bootstrapEnvironment

  把①的值和发起事件传递过来的环境对象(为子容器的环境对象)的所有值都加入到此boostrap上下文的的环境

  添加BOOTSTRAP_PROPERTY_SOURCE_NAME

③WebApplicationType为NONE,这样就不会创建内嵌的服务器

④添加了BootstrapImportSelectorConfiguration为primarySources,会SPI导入的BootstrapConfiguration类型指定的指定配置类,加入父容器中

  - 在spring-cloud-context包里的指定,  其中比较重要的是PropertySourceBootstrapConfiguration

    

   - Nocas框架包中的指定,NacosConfigBootstrapConfiguration

    

* 执行此新的boostrap的SpringApplication的run方法

* 把父上下文context设置到子上下文(触发此事件的Context)的父上下文属性中

* 把父上下文的环境bootstrapProperties加入到environment(子上下文的),作为父子上下文唯一的环境

  如果父环境存在springCloudDefaultProperties属性配置,需要取覆盖environment(子上下文的)对于的属性

* 从父容器中的找到ApplicationContextInitializer类型的bean,设置进去子容器中application.setInitializers(target);让子容器去执行

  - 比如(ApplicationContextInitializer)PropertySourceBootstrapConfiguration,它会从容器中找到List<PropertySourceLocator> propertySourceLocators

遍历调用locator.locateCollection(environment)获取配置加入到environment中,

  - 而父容器中PropertySourceLocator类型的实例一般也是通过BootstrapImportSelectorConfiguration的import注册的

比如Nacos的boostrap配置类NacosConfigBootstrapConfiguration中就有一个@Bean方法PropertySourceLocator从Nacos中加载

@Configuration
@ConditionalOnProperty(name = "spring.cloud.nacos.config.enabled", matchIfMissing = true)
public class NacosConfigBootstrapConfiguration {

   @Bean
   @ConditionalOnMissingBean
   public NacosConfigProperties nacosConfigProperties() {
      return new NacosConfigProperties();
   }

   @Bean
   public NacosPropertySourceLocator nacosPropertySourceLocator(
         NacosConfigProperties nacosConfigProperties) {
      return new NacosPropertySourceLocator(nacosConfigProperties);
   }

}

* 注:在父容器阶段,也是会走BoostrapApplicationListener,但是传递进来的environment存在BOOTSTRAP_PROPERTY_SOURCE_NAME,说明已经处理,结束

也会ConfigFileApplicationListener解析spring.config.name指定的名为boostrap的文件

public class BootstrapApplicationListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent>, Ordered {

   //优先级比ConfigFileApplicationListener
   public static final int DEFAULT_ORDER = Ordered.HIGHEST_PRECEDENCE + 5;
   public static final String BOOTSTRAP_PROPERTY_SOURCE_NAME = "bootstrap";
   public static final String DEFAULT_PROPERTIES = "springCloudDefaultProperties";
   private int order = DEFAULT_ORDER;

   @Override
   public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
      ConfigurableEnvironment environment = event.getEnvironment();
      if (!bootstrapEnabled(environment) && !useLegacyProcessing(environment)) {
         return;
      }
      //如果中environment存在BOOTSTRAP_PROPERTY_SOURCE_NAME,说明已经处理,结束
      if (environment.getPropertySources().contains(BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
         return;
      }
      ConfigurableApplicationContext context = null;
      //去环境变量spring.cloud.bootstrap.name,默认为bootstrap
      String configName = environment.resolvePlaceholders("${spring.cloud.bootstrap.name:bootstrap}");
      //ParentContextApplicationContextInitializer初始化器的处理,一般为空
      for (ApplicationContextInitializer<?> initializer : event.getSpringApplication().getInitializers()) {
         if (initializer instanceof ParentContextApplicationContextInitializer) {
            context = findBootstrapContext((ParentContextApplicationContextInitializer) initializer, configName);
         }
      }
      //生成bootstrap上下文
      if (context == null) {
         context = bootstrapServiceContext(environment, event.getSpringApplication(), configName);
         event.getSpringApplication().addListeners(new CloseContextOnFailureApplicationListener(context));
      }
      //初始化器的调用。这里主要是从父容器中额外的找到ApplicationContextInitializer类型,比如PropertySourceBootstrapConfiguration
      //设置进去子容器中application.setInitializers(target);让子容器去执行
      //Set target = new LinkedHashSet<>(application.getInitializers());
      //    target.addAll(getOrderedBeansOfType(context, ApplicationContextInitializer.class));
      //    application.setInitializers(target);
      apply(context, event.getSpringApplication(), environment);
   }

   
   private ConfigurableApplicationContext bootstrapServiceContext(ConfigurableEnvironment environment,
         final SpringApplication application, String configName) {

      //父上下文的的环境
      ConfigurableEnvironment bootstrapEnvironment = new AbstractEnvironment() {};

      //收集环境变量中spring.cloud.bootstrap.locationspring.cloud.bootstrap.additional-location
      //这里都是在ConfigFileApplicationListener中解析boostrap文件会用到
      MutablePropertySources bootstrapProperties = bootstrapEnvironment.getPropertySources();
      String configLocation = environment.resolvePlaceholders("${spring.cloud.bootstrap.location:}");
      String configAdditionalLocation = environment.resolvePlaceholders("${spring.cloud.bootstrap.additional-location:}");
      Map<String, Object> bootstrapMap = new HashMap<>();
      bootstrapMap.put("spring.config.name", configName);
      bootstrapMap.put("spring.main.web-application-type", "none");
      if (StringUtils.hasText(configLocation)) {
         bootstrapMap.put("spring.config.location", configLocation);
      }
      if (StringUtils.hasText(configAdditionalLocation)) {
         bootstrapMap.put("spring.config.additional-location", configAdditionalLocation);
      }
      bootstrapProperties.addFirst(new MapPropertySource(BOOTSTRAP_PROPERTY_SOURCE_NAME, bootstrapMap));
      for (PropertySource<?> source : environment.getPropertySources()) {
         if (source instanceof StubPropertySource) {
            continue;
         }
         bootstrapProperties.addLast(source);
      }

        //构建父上下文SpringApplication:指定好ActiveProfiles、父上下文的的环境、WebApplicationType.NONE(不会创建服务器)
      SpringApplicationBuilder builder = new SpringApplicationBuilder().profiles(environment.getActiveProfiles())
            .bannerMode(Mode.OFF).environment(bootstrapEnvironment)
            .registerShutdownHook(false).logStartupInfo(false).web(WebApplicationType.NONE);
      final SpringApplication builderApplication = builder.application();
      if (builderApplication.getMainApplicationClass() == null) {
         builder.main(application.getMainApplicationClass());
      }
      if (environment.getPropertySources().contains("refreshArgs")) {
         builderApplication.setListeners(filterListeners(builderApplication.getListeners()));
      }
      //添加一个Import,在这里会先父容器注入 从spi中找到的所有BootstrapConfiguration指定的类型
      builder.sources(BootstrapImportSelectorConfiguration.class);
      final ConfigurableApplicationContext context = builder.run();
      context.setId("bootstrap");
      //添加AncestorInitializerc初始化器,其作用是
      //把父上下文context设置到子上下文的父中
      addAncestorInitializer(application, context);
      bootstrapProperties.remove(BOOTSTRAP_PROPERTY_SOURCE_NAME);
      //把父上下文的环境bootstrapProperties加入到environment(子上下文的)作为父子上下文唯一的环境
      //environment.addLast(result);
      mergeDefaultProperties(environment.getPropertySources(), bootstrapProperties);
      return context;
   }

   //...

}

ConfigFileApplicationListener加载application文件

* ConfigFileApplicationListener监听到ApplicationEnvironmentPreparedEvent事件后

①ConfigFileApplicationListener获取META-INF/spring.factories下的EnvironmentPostProcessor类型对象集合

  ②遍历调用所有EnvironmentPostProcessor的postProcessEnvironment方法

  注意这里又回到了ConfigFileApplicationListener因为其同时实现了EnvironmentPostProcessor, SmartApplicationListener接口

  


private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
    List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
    postProcessors.add(this);
    AnnotationAwareOrderComparator.sort(postProcessors);
    for (EnvironmentPostProcessor postProcessor : postProcessors) {
        postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());
    }
}
//ConfigFileApplicationListener
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
    addPropertySources(environment, application.getResourceLoader());
}
protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
    RandomValuePropertySource.addToEnvironment(environment);
    new Loader(environment, resourceLoader).load();
}

* ConfigFileApplicationListener的postProcessEnvironment进行配置文件的解析,基于.ConfigFileApplicationListener.Loader对象进行解析
  ①去除environment中key为defaultProperties的PropertySources中spring.profiles.include和pring.profiles.active,不去影响下面的consumer的执行

等下面的consumer的执行后再补上
  ②initializeProfiles方法从多个配置源加载设置的Deque<ConfigFileApplicationListener.Profile profile> profiles,顺序为
    - 用户直接通过environment对象.setActiveProfiles(String... profiles)指定的
    - 从environment中可以通过属性名spring.profiles.include指定的(spring.profiles.active.name = fat),封装为Profile,defaultProfile=flase;
    - 从environment中可以通过属性名spring.profiles.active指定profile指定的,封装为Profile,defaultProfile=flase
    - 去除profile中defaultProfile=true默认的

- 以上获取为null,且用户如果没有直接设置Environment的setDefaultProfiles,那么就取spring.profiles.default配置的

  Profile defaultProfile = new Profile(defaultProfileName, true);  this.profiles.add(defaultProfile);


  ③遍历环境指定的profile的,在指定路径读取配置文件{spring.config.name}-{profile}.{xml/yam等},addLast的形式
    - 此profile不是默认文件时,添加environment.addActiveProfile(profile)

- 获得加载配置文件的路径
      可以通过spring.config.location配置设置路径,如果没有配置,则使用默认
      默认路径由DEFAULT_SEARCH_LOCATIONS指定 + spring.config.additional-location属性指定,默认为 "classpath:/,classpath:/config/,file:./,file:./config/"

- 查找配置文件名,可以通过spring.config.name指定文件名,如果没有设置,使用默认名application
    - 遍历处理每一个文件后缀类型,为properties、xml、yml或者yaml的文件,

- 拼接文件名-profile.文件后缀的文件路径

      通过resourceLoader读取一个配置文件内容,将其封装到Document类(对应一个文件)中,使用loader,

如properties解析根据PropertiesPropertySourceLoader解析成List<PropertySource<?>>,从此文件内获取此文件指定的spring.profiles.*
      在封装为Document(PropertySource<?>数据源, spring.profiles.*值, spring.profiles.activte值, spring.profiles.include值),缓存起来

      如下几种文件是可以解析的,对此document开始解析为MutablePropertySources,后插到 Map<ConfigFileApplicationListener.Profile, MutablePropertySources> loaded

      * 文件名(默认application).文件存在,但文件内spring.profiles.*属性不空且和传递进来的profile一致且和environment设置的有效profile一致(为空就判断是否为默认)

* 文件名(默认application)-profile.文件存在,但文件内spring.profiles.*属性为空

      * 文件名(默认application)-profile.文件存在,但文件内spring.profiles.*属性不空且和传递进来的profile一致和environment设置的有效profile一致(为空就判断是否为默认)

* 文件名(默认application)profile.文件存在,但文件内spring.profiles.*属性不空且和传递进来的profile一致和environment设置的有效profile一致(为空就判断是否为默认)

* 其余文件后缀的同理

   ④不传递profile,逻辑和③差不多,只扫描默认文件,且和传递进来的profile一致这个条件不要

否同时,只要此文件中存在之前加载过文件中同名的属性,那么此文件也不会被加载

   ⑤将配置文件作为配置源添加到Environment对象中,其位置位于key为defaultProperties的之上

* 文件加载顺序

   - 嵌套遍历:遍历profile的()顺序见②,遍历路径,遍历文件名 --- 由于是addLast的模式,扫描文件的顺序其实和enviroment获取的优先级相反

   - 由于在最后一步会reverse所有属性,在set进行add(后面重复key的就丢弃),再addLast到enviroment中,所以属性取值的优先级从高到低,如下

file:./config/ (当前项目路径config目录下); yml/yaml -> properties/xml

      file:./ (当前项目路径下);

      classpath:/config/ (类路径-去resources下config目录下);

      classpath:/ (类路径config下).

private class Loader {

    private final Log logger = ConfigFileApplicationListener.this.logger;

    private final ConfigurableEnvironment environment;

    private final PropertySourcesPlaceholdersResolver placeholdersResolver;

    private final ResourceLoader resourceLoader;

    private final List<PropertySourceLoader> propertySourceLoaders;

    private Deque<ConfigFileApplicationListener.Profile> profiles;

    private List<ConfigFileApplicationListener.Profile> processedProfiles;

    private boolean activatedProfiles;

    private Map<ConfigFileApplicationListener.Profile, MutablePropertySources> loaded;

    private Map<ConfigFileApplicationListener.DocumentsCacheKey, List<ConfigFileApplicationListener.Document>> loadDocumentsCache = new HashMap<>();

    Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
        this.environment = environment;
        this.placeholdersResolver = new PropertySourcesPlaceholdersResolver(this.environment);
        this.resourceLoader = (resourceLoader != null) ? resourceLoader : new DefaultResourceLoader();
        this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class,
                getClass().getClassLoader());
    }


static void apply(ConfigurableEnvironment environment, String propertySourceName, Set<String> filteredProperties,
                  Consumer<PropertySource<?>> operation) {
    MutablePropertySources propertySources = environment.getPropertySources();
    PropertySource<?> original = propertySources.get(propertySourceName);
    //如果keydefaultProperties不存在,直接执行operation
    if (original == null) {
        operation.accept(null);
        return;
    }
    //如果存在,去除其中的defaultPropertiesspring.profiles.includepring.profiles.active
    //在执行operation
    propertySources.replace(propertySourceName, new FilteredPropertySource(original, filteredProperties));
    try {
        operation.accept(original);
    }
    finally {
        propertySources.replace(propertySourceName, original);
    }
}


    void load() {
        //apply:从environment获取keydefaultProperties的值PropertySource<?> original,如果为空直接执行consumer
        // 否则,在original底层数据源中中去除了keyLOAD_FILTERED_PROPERTY指定的值,original作为参数执行consumer
        //最终使用把environmentkeydefaultProperties的值替换为处理之后的original
        //其作用是暂时去除defaultPropertiesspring.profiles.includepring.profiles.active,不去需要consumer的执行
        //
        FilteredPropertySource.apply(this.environment, DEFAULT_PROPERTIES, LOAD_FILTERED_PROPERTY,
                (defaultProperties) -> {
                    this.profiles = new LinkedList<>();
                    this.processedProfiles = new LinkedList<>();
                    this.activatedProfiles = false;
                    this.loaded = new LinkedHashMap<>();
                    //initializeProfiles从多个配置源加载设置的Deque<ConfigFileApplicationListener.Profile profile
                    //配置源可以是:环境变量、启动参数"--"设置、Environment对象设置等,顺序为
                    //用户直接通过environment对象.setActiveProfiles(String... profiles)指定的
                    //environment中可以通过属性名spring.profiles.include指定的
                    //environment中可以通过属性名spring.profiles.active指定profile指定的
                    //去除profile中默认的,添加environment对象中保存的(用户没有进行setter话,值和从environment中可以通过属性名spring.profiles.default获取的一样)
                    //无论上述配置源没有设置profile,都会在profiles属性中增加null,这是为了保证能首先处理默认的配置文件
                    initializeProfiles();
                    while (!this.profiles.isEmpty()) {
                        ConfigFileApplicationListener.Profile profile = this.profiles.poll();
                        //默认profiles的,进行environment.addActiveProfile
                        if (isDefaultProfile(profile)) {
                            addProfileToEnvironment(profile.getName());
                        }
                        //读取配置文件application-profile.xml/yamladdLast的形式
                        load(profile, this::getPositiveProfileFilter,addToLoaded(MutablePropertySources::addLast, false));
                        this.processedProfiles.add(profile);
                    }
                    //读取配置文件application.xml/yamladdLast的形式,如果没有配置spring.profiles属性,那么下面这个方法不会加载任何内容
                    load(null, this::getNegativeProfileFilter, addToLoaded(MutablePropertySources::addFirst, true));
                    //将配置文件作为配置源添加到Environment对象中,其位置位于keydefaultProperties的之上
                    addLoadedPropertySources();
                    //实例this.processedProfiles中所有非默认标志的Profile对象。取第一个设置到this.environment.setActiveProfiles(activeProfiles.toArray(new String[0]));
                    applyActiveProfiles(defaultProperties);
                });
    }


    private void load(ConfigFileApplicationListener.Profile profile, ConfigFileApplicationListener.DocumentFilterFactory filterFactory, ConfigFileApplicationListener.DocumentConsumer consumer) {
        ////获得加载配置文件的路径
        // 可以通过spring.config.location配置设置路径,如果没有配置,则使用默认
        // 默认路径由DEFAULT_SEARCH_LOCATIONS指定 + spring.config.additional-location属性指定
        // String DEFAULT_SEARCH_LOCATIONS = "classpath:/,classpath:/config/,file:./,file:./config/"
        getSearchLocations().forEach((location) -> {
            boolean isFolder = location.endsWith("/");
            ////查找配置文件名,可以通过spring.config.name指定文件名,如果没有设置,使用默认名application
            Set<String> names = isFolder ? getSearchNames() : NO_SEARCH_NAMES;
            names.forEach((name) -> load(location, name, profile, filterFactory, consumer));
        });
    }

    private void load(String location, String name, ConfigFileApplicationListener.Profile profile, ConfigFileApplicationListener.DocumentFilterFactory filterFactory,
                      ConfigFileApplicationListener.DocumentConsumer consumer) {
        //除非我们设置spring.config.name为空或者null
        //或者是spring.config.location指定了配置文件的完整路径,也就是入参location的值
        if (!StringUtils.hasText(name)) {
            for (PropertySourceLoader loader : this.propertySourceLoaders) {
                if (canLoadFileExtension(loader, location)) {
                    load(loader, location, profile, filterFactory.getDocumentFilter(profile), consumer);
                    return;
                }
            }
            throw new IllegalStateException("");
        }

        //propertySourceLoaders属性是在Load类的构造方法中设置的,遍历可以加载文件后缀为propertiesxmlyml或者yaml的文件
        Set<String> processed = new HashSet<>();
        for (PropertySourceLoader loader : this.propertySourceLoaders) {
            for (String fileExtension : loader.getFileExtensions()) {
                if (processed.add(fileExtension)) {
                    //将路径、文件名、后缀组合起来形成完成文件名
                    loadForFileExtension(loader, location + name, "." + fileExtension, profile, filterFactory,
                            consumer);
                }
            }
        }
    }

    private boolean canLoadFileExtension(PropertySourceLoader loader, String name) {
        return Arrays.stream(loader.getFileExtensions())
                .anyMatch((fileExtension) -> StringUtils.endsWithIgnoreCase(name, fileExtension));
    }

    private void loadForFileExtension(PropertySourceLoader loader, String prefix, String fileExtension,
                                      ConfigFileApplicationListener.Profile profile, ConfigFileApplicationListener.DocumentFilterFactory filterFactory, ConfigFileApplicationListener.DocumentConsumer consumer) {
        //PositiveProfileFilter
        // profile == null,判断document.getProfiles()是否为空
        // profile = null,判断document.getProfiles()是和指定的意义且是environment设置的有效profile(为空就判断是否为默认)
        ConfigFileApplicationListener.DocumentFilter defaultFilter = filterFactory.getDocumentFilter(null);
        ConfigFileApplicationListener.DocumentFilter profileFilter = filterFactory.getDocumentFilter(profile);
        if (profile != null) {
            //拼接文件名-profile.文件后缀
            String profileSpecificFile = prefix + "-" + profile + fileExtension;
            //读取文件中Profiles没有配置的
            load(loader, profileSpecificFile, profile, defaultFilter, consumer);
            //读取文件中Profiles有配置且是environment设置的有效profile(为空就判断是否为默认)
            load(loader, profileSpecificFile, profile, profileFilter, consumer);
            //再检查一次已经处理的Profile
            for (ConfigFileApplicationListener.Profile processedProfile : this.processedProfiles) {
                if (processedProfile != null) {
                    String previouslyLoaded = prefix + "-" + processedProfile + fileExtension;
                    load(loader, previouslyLoaded, profile, profileFilter, consumer);
                }
            }
        }
        //尝试加载下忘记添加"-"的文件
        load(loader, prefix + fileExtension, profile, profileFilter, consumer);
    }

    private void load(PropertySourceLoader loader, String location, ConfigFileApplicationListener.Profile profile, ConfigFileApplicationListener.DocumentFilter filter,
                      ConfigFileApplicationListener.DocumentConsumer consumer) {
        try {
            //调用Resource类加载配置文件
            Resource resource = this.resourceLoader.getResource(location);
            if (resource == null || !resource.exists()) {
                return;
            }
            if (!StringUtils.hasText(StringUtils.getFilenameExtension(resource.getFilename()))) {
                return;
            }

            //读取一个配置文件内容,将其封装到Document类中,使用loader,如properties解析根据PropertiesPropertySourceLoader解析成List<PropertySource<?>>
            // 在封装为Document(PropertySource<?>数据源, 当前文件spring.profiles, spring.profiles.active, spring.profiles.include
            String name = "applicationConfig: [" + location + "]";
            List<ConfigFileApplicationListener.Document> documents = loadDocuments(loader, name, resource);
            if (CollectionUtils.isEmpty(documents)) {
                return;
            }
            List<ConfigFileApplicationListener.Document> loaded = new ArrayList<>();
            for (ConfigFileApplicationListener.Document document : documents) {
                if (filter.match(document)) {
                    //加入this.profiles.addAll(profiles);profiles并移除默认的profile
                    addActiveProfiles(document.getActiveProfiles());
                    //同样加入this.profiles.
                    addIncludedProfiles(document.getIncludeProfiles());
                    loaded.add(document);
                }
            }
            //开始加入loadedMutablePropertySources
            Collections.reverse(loaded);
            if (!loaded.isEmpty()) {
                loaded.forEach((document) -> consumer.accept(profile, document));
            }
        }
        catch (Exception ex) {
            throw new IllegalStateException("Failed to load property source from location '" + location + "'", ex);
        }
    }




}

更多推荐