先介绍Spring Cloud留下的扩展点,和整个加载流程。后面文章会以具体配置中心举例进行说明。
一、
PropertySourceBootstrapConfiguration 核心功能介绍
注入PropertySourceLocator实现类,PropertySourceLocator是一个接口,加载配置的扩展点。
@Autowired(required = false)
private List propertySourceLocators = new ArrayList<>();
核心实现代码如下(做了删减方便说明核心功能)
//ApplicationContextInitializer 接口的实现方法
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
List> composite = new ArrayList<>();
boolean empty = true;
ConfigurableEnvironment environment = applicationContext.getEnvironment();
for (PropertySourceLocator locator : this.propertySourceLocators) {
//重要!!调用扩展点实现类,获得配置
Collection> source = locator.locateCollection(environment);
List> sourceList = new ArrayList<>();
for (PropertySource> p : source) {
sourceList.add(new SimpleBootstrapPropertySource(p));
}
logger.info("Located property source: " + sourceList);
composite.addAll(sourceList);
empty = false;
}
if (!empty) {
MutablePropertySources propertySources = environment.getPropertySources();
//重要!! 合并配置到当前environment中
insertPropertySources(propertySources, composite);
}
}
二、initialize方法是何时被调用的?
整体过程比较绕,会涉及到bootstrap的 SpringApplication,和application的SpringApplication加载过程。
在SpringApplication的run方法中
//创建applicationContext
context = createApplicationContext();
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
//调用ApplicationContextInitializer
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);
}
}
PropertySourceBootstrapConfiguration实现
ApplicationContextInitializer接口,所以
PropertySourceBootstrapConfiguration的initialize(context)方法会被调用,进而触发配置加载扩展点的调用。
其实没那么简单,上面看到的只是比较明确的调用过程。
问一个问题,getInitializers() 是如何获取到
PropertySourceBootstrapConfiguration的?
public Set> getInitializers() {
return asUnmodifiableOrderedSet(this.initializers);
}
在SpringApplication的构建中,我们看到从META-INF/spring.factories中加载
ApplicationContextInitializer,找完所有的文件后,你会发现
PropertySourceBootstrapConfiguration并没有配置在里面。
public SpringApplication(ResourceLoader resourceLoader, Class>... primarySources) {
//其他代码省略
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
}
回到上一节的
BootstrapApplicationListener中,我们知道bootstrap 的SpringApplication在这里进行创建,这里sources是关键。
builder.sources(BootstrapImportSelectorConfiguration.class);
在bootstrap的SpringApplication中会导入BootstrapImportSelector
@Configuration(proxyBeanMethods = false)
@Import(BootstrapImportSelector.class)
public class BootstrapImportSelectorConfiguration {
}
BootstrapImportSelector会从META-INF/spring.factories加载BootstrapConfiguration的类
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// Use names and ensure unique to protect against duplicates
List names = new ArrayList<>(SpringFactoriesLoader
.loadFactoryNames(BootstrapConfiguration.class, classLoader));
names.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(
this.environment.getProperty("spring.cloud.bootstrap.sources", ""))));
//其他代码省略
return classNames;
}
# Bootstrap components
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration,\
这个时候你会发现
PropertySourceBootstrapConfiguration类被加载到bootstrap的SpringApplication中。但是这里只是被加载,还没有被调用。
回到
BootstrapApplicationListener中,继续往下看
private void apply(ConfigurableApplicationContext context,
SpringApplication application, ConfigurableEnvironment environment) {
Set target = new LinkedHashSet<>(application.getInitializers());
//获取bootstrap SpringApplication中所有ApplicationContextInitializer类
target.addAll(getOrderedBeansOfType(context, ApplicationContextInitializer.class));
//加入到application SpringApplication中
application.setInitializers(target);
}
这时application SpringApplication的initializers就有了
PropertySourceBootstrapConfiguration,下面就顺理成章的被调用了。
三、总结
1.application的SpringApplication构建中触发environmentPrepared事件。
2.environmentPrepared事件触发bootstrap 的SpringApplication构建。
3.bootstrap 的SpringApplication会创建并配置好
PropertySourceBootstrapConfiguration对象供application的SpringApplication使用。
4.application的SpringApplication在prepareContext时,触发
PropertySourceBootstrapConfiguration.initialize()方法调用。
四、Q&A
Q:为什么SpringApplication直接加载
PropertySourceBootstrapConfiguration不可行?
public SpringApplication(ResourceLoader resourceLoader, Class>... primarySources) {
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
}
A:因为
PropertySourceBootstrapConfiguration中使用了@Autowired 注入PropertySourceLocator的实现类,直接从配置文件中加载,调用initialize()时,ApplicationContext还没准备好,无法实现自动注入,则 propertySourceLocators永远为空,无法实现扩展功能。
Q:怎么理解bootstrap SpringApplication
A:bootstrap SpringApplication其实是application SpringApplication的父容器,可以理解为bootstrap要准备好各种application初始化过程用要用到的配置和对象,供其使用。