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