1. 前言
本文基于Spring Cloud Version:Spring Cloud Hoxton.SR8,Spring Boot Version:2.3.2.RELEASE
在spring boot中
Environment
的扩展配置可借助
ConfigFileApplicationListener
这个
ApplicationListener
,通过自定义类实现
EnvironmentPostProcessor
接口来扩展,详细介绍可以我之前的文章《spring boot参数配置之Environment源码分析》、《spring boot参数配置之Environment扩展》.
在spring cloud中
Environment
的扩展配置还可以借助
PropertySourceBootstrapConfiguration
这个
ApplicationContextInitializer
,通过自定义类实现
PropertySourceLocator
接口来扩展。
2. spring cloud 启动处理Environment源码分析
2.1. spring cloud 启动ApplicationContext两阶段创建
在spring cloud中启动时
SpringApplication.run(String... args)
会执行两次,也就是说
ApplicationContext
会分为两阶段来创建。
- 第一次执行run(阶段一):阶段一执行会只会加载
里配置的参数以及创建bootstrap.yml
中spring.factories
里配置的类,这些类往往是一些跟配置相关的类,这一阶段创建的类优先级很高,主要为阶段二org.springframework.cloud.bootstrap.BootstrapConfiguration
ApplicationContext
里需要创建的类提供一些准备。
可以看看
里的spring-cloud-context-2.2.5.RELEASE.jar
中spring.factories
相关配置# Bootstrap components
- 第二次执行run(阶段二):阶段二执行会加载剩余所有的参数以及创建所有的bea对象,主要是通过@ComponentScan可以扫描到的、xml里配置的bean、以及通过
加载@EnableAutoConfiguration
里的spring.factories
的相关配置org.springframework.boot.autoconfigure.EnableAutoConfiguration
可以看看
spring-cloud-context-2.2.5.RELEASE.jar
里的
spring.factories
中
# AutoConfiguration
相关配置
# AutoConfiguration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration,\
org.springframework.cloud.autoconfigure.LifecycleMvcEndpointAutoConfiguration,\
org.springframework.cloud.autoconfigure.RefreshAutoConfiguration,\
org.springframework.cloud.autoconfigure.RefreshEndpointAutoConfiguration,\
org.springframework.cloud.autoconfigure.WritableEnvironmentEndpointAutoConfiguration
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.cloud.bootstrap.BootstrapApplicationListener,\
org.springframework.cloud.bootstrap.LoggingSystemShutdownListener,\
org.springframework.cloud.context.restart.RestartListener
# Bootstrap components
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration,\
org.springframework.cloud.bootstrap.encrypt.EncryptionBootstrapConfiguration,\
org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.cloud.util.random.CachedRandomPropertySourceAutoConfiguration
2.2. spring cloud 启动参数相关源码分析
主线:
PropertySourceBootstrapConfiguration的initialize(…)->PropertySourceLocator.locate()
核心源码如下:
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//environment参数创建准备工作
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach(environment);
//这里触发再一次执行上面的run方法,也就是出发第一阶段applicationContest创建的创建
listeners.environmentPrepared(environment);
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
applyInitializers(context);
…………
}
protected void applyInitializers(ConfigurableApplicationContext context) {
for (ApplicationContextInitializer initializer : getInitializers()) {
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
ApplicationContextInitializer.class);
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
initializer.initialize(context);
}
}
这个代码的详细分析可以参见文章《spring boot参数配置之Environment源码分析》。
PropertySourceBootstrapConfiguration
的创建
阶段一在refreshContext之前,SpringApplication中所有的
initializers
截图如下,并没有
PropertySourceBootstrapConfiguration
。

从上面
spring-cloud-context-2.2.5.RELEASE.jar
里的
spring.factories
的配置有
org.springframework.cloud.bootstrap.BootstrapConfiguration=\org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration
,所以在第一阶段执行完后,SpringApplication中所有的
initializers
截图如下,以包含
PropertySourceBootstrapConfiguration
。
这一阶段同样需要创建自定义的
PropertySourceLocator
对象,如:
NacosPropertySourceLocator
实现类,其配置如下:
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
com.alibaba.cloud.nacos.NacosConfigBootstrapConfiguration
PropertySourceBootstrapConfiguration的initialize(…)->PropertySourceLocator.locate()
执行
阶段二的
applyInitializers
方法会调用
PropertySourceBootstrapConfiguration
的
initialize
方法,核心代码如下:
public void initialize(ConfigurableApplicationContext applicationContext) {
List<PropertySource<?>> composite = new ArrayList<>();
AnnotationAwareOrderComparator.sort(this.propertySourceLocators);
boolean empty = true;
ConfigurableEnvironment environment = applicationContext.getEnvironment();
for (PropertySourceLocator locator : this.propertySourceLocators) {
Collection<PropertySource<?>> source = locator.locateCollection(environment);
if (source == null || source.size() == 0) {
continue;
}
List<PropertySource<?>> sourceList = new ArrayList<>();
for (PropertySource<?> p : source) {
if (p instanceof EnumerablePropertySource) {
EnumerablePropertySource<?> enumerable = (EnumerablePropertySource<?>) p;
sourceList.add(new BootstrapPropertySource<>(enumerable));
}
else {
sourceList.add(new SimpleBootstrapPropertySource(p));
}
}
logger.info("Located property source: " + sourceList);
composite.addAll(sourceList);
empty = false;
}
if (!empty) {
MutablePropertySources propertySources = environment.getPropertySources();
String logConfig = environment.resolvePlaceholders("${logging.config:}");
LogFile logFile = LogFile.get(environment);
for (PropertySource<?> p : environment.getPropertySources()) {
if (p.getName().startsWith(BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
propertySources.remove(p.getName());
}
}
insertPropertySources(propertySources, composite);
reinitializeLoggingSystem(environment, logConfig, logFile);
setLogLevels(applicationContext, environment);
handleIncludedProfiles(environment);
}
}
核心逻辑就是遍历所有
propertySourceLocators
执行其
locate(……)
方法,将自定义加载的
CompositePropertySource
然后追加到spring中的
environment
中。
2.3. 自定义 PropertySourceLocator
扩展environment实例
PropertySourceLocator
PropertySourceLocator
实现如下:
public class CustPropertySourceLocator implements PropertySourceLocator {
@Override
public PropertySource<?> locate(Environment environment) {
//可以从自己的配置中心获取数据
Map<String, Object> configs =new HashMap<>();
configs.put("metric.post.url","http://baidu.com");
return new MapPropertySource("MY_CONFIG_CENTER",configs);
}
}
spring.factories
配置如下:
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
com.example.nacoshttpproducer.configure.CustPropertySourceLocator