SpringBoot源码笔记1——SpringApplication类run方法
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指定类型配置的所有对象,如下获取Bootstrapper、ApplicationContextInitializer、ApplicationListener接口的所有配置的实现类对象
//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<?>> instanceSuppliers,Map<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);
//把environment中name为spring.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);
//从容器中获取ApplicationRunner、CommandLineRunner调用其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) {
//创建ConfigurableEnvironment,servlet环境下对应StandardServletEnvironment
ConfigurableEnvironment environment = getOrCreateEnvironment();
//此类的addConversionService为true时,为environment.setConversionService(ApplicationConversionService对象)
//此类的Map<String, Object> defaultProperties不为空时,转为DefaultPropertiesPropertySource后addLast到environment
//此类的addCommandLineProperties为且args不为空时,把命令行参数封装为SimpleCommandLinePropertySource后addFirst到environment
//configureProfiles(ConfigurableEnvironment environment, String[] args) 方法供子类实现
configureEnvironment(environment, applicationArguments.getSourceArgs());
//把当前environment下的MutablePropertySources的名称转为spring配置key的标准格式,如my.server_name.PORT会转为 my.servername.port
//再组装成一个大的PropertySource集合,那么name为configurationProperties,类型为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(在创建的时候,会把当前SpringApplication的listeners所有监听器注册进来)的environmentPrepared会发布ApplicationEnvironmentPreparedEvent事件
//EnvironmentPostProcessorApplicationListener监听了ApplicationEnvironmentPreparedEvent事件会,会把META-INF/spring.factories下的EnvironmentPostProcessorApplicationListener对象执行其postProcessEnvironment方法
//其中ConfigFileApplicationListener对象的postProcessEnvironment对象就是加载类路径下application.propertise/yml文件
listeners.environmentPrepared(bootstrapContext, environment);
//把那么为defaultProperties的PropertySource移到最后
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
// 设置environment、ResourceLoader
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());
}
// 加载primarySources(run方法参数指定)和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);
//基于@Order、Priority注解的排序
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;
}
//setter、getter
}
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.location、spring.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);
//如果key为defaultProperties不存在,直接执行operation
if (original == null) {
operation.accept(null);
return;
}
//如果存在,去除其中的defaultProperties中spring.profiles.include和pring.profiles.active
//在执行operation
propertySources.replace(propertySourceName, new FilteredPropertySource(original, filteredProperties));
try {
operation.accept(original);
}
finally {
propertySources.replace(propertySourceName, original);
}
}
void load() {
//apply:从environment获取key为defaultProperties的值PropertySource<?> original,如果为空直接执行consumer;
// 否则,在original底层数据源中中去除了key为LOAD_FILTERED_PROPERTY指定的值,original作为参数执行consumer
//最终使用把environment中key为defaultProperties的值替换为处理之后的original
//其作用是暂时去除defaultProperties中spring.profiles.include和pring.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/yaml,addLast的形式
load(profile, this::getPositiveProfileFilter,addToLoaded(MutablePropertySources::addLast, false));
this.processedProfiles.add(profile);
}
//读取配置文件application.xml/yaml,addLast的形式,如果没有配置spring.profiles属性,那么下面这个方法不会加载任何内容
load(null, this::getNegativeProfileFilter, addToLoaded(MutablePropertySources::addFirst, true));
//将配置文件作为配置源添加到Environment对象中,其位置位于key为defaultProperties的之上
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类的构造方法中设置的,遍历可以加载文件后缀为properties、xml、yml或者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);
}
}
//开始加入loaded的MutablePropertySources
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);
}
}
}
更多推荐
所有评论(0)