1、spring cloud 父子容器整体关系
由于在spring cloud启动源码加载过程中,没太弄清楚tomcat容器,spring 容器,spring cloud容器整体的加载顺序以及关系,所以想分析下这个流程,先不多说,上图
容器大致分为三层:
BootStrap Spring 容器:由SpringCloud 监听器创建,用来初始化 SpringCloud 上下文
SpringBoot Spring 容器:由SpringBoot创建,也是项目中常用的Spring容器。
微服务 Spring相关容器:Feign和Ribbon配置类对应的上下文,由配置容器抽象工厂 NamedContextFactory 创建,用于容器隔离。
先说spring cloud容器bootstrap,在容器启动时,会先读取boostrap.xml文件,进行基本配置的加载,随后才会加载application.xml,这其实也说明了bootstrap容器是先于spring boot容器的,本质其实是spring 容器的环境配置是依赖于bootstrap的,bootstrap上下文是主应用程序的父上下文共享一个 Environment,它是任何Spring应用程序的外部属性的来源。要注意的是,bootstrap容器的配置优先级要高于spring boot,这种优先级不但体现于加载顺序,还有就是当俩者都存在某一项配置,boostrap配置是不会被覆盖,即以bootstrap配置为主
废话不多说,先看源码
SpringBoot 在启动时,会触发相关一系列监听器,监听器各司其职,做一些初始化预处理操作。SpringCloud 实现了自己的监听器:BootstrapApplicationListener,来初始化SpringCloud上下文环境。
@Override
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
ConfigurableEnvironment environment = event.getEnvironment();
//如果未开启SpringCloud,直接返回
if (!environment.getProperty("spring.cloud.bootstrap.enabled", Boolean.class,
true)) {
return;
}
// don't listen to events in a bootstrap context
//判断该监听器是否已经执行过,如果执行过,直接返回
if (environment.getPropertySources().contains(BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
return;
}
//这里返回了一个 Spring 容器
ConfigurableApplicationContext context = bootstrapServiceContext(environment,
event.getSpringApplication());
apply(context, event.getSpringApplication(), environment);
}
bootstrapServiceContext方法创建了一个 Spring 容器:ConfigurableApplicationContext:
private ConfigurableApplicationContext bootstrapServiceContext(
ConfigurableEnvironment environment, final SpringApplication application) {
StandardEnvironment bootstrapEnvironment = new StandardEnvironment();
MutablePropertySources bootstrapProperties = bootstrapEnvironment
.getPropertySources();
for (PropertySource<?> source : bootstrapProperties) {
bootstrapProperties.remove(source.getName());
}
//设置读取 bootstrap 文件
String configName = environment
.resolvePlaceholders("${spring.cloud.bootstrap.name:bootstrap}");
//设置 bootstrap 文件路径
String configLocation = environment
.resolvePlaceholders("${spring.cloud.bootstrap.location:}");
Map<String, Object> bootstrapMap = new HashMap<>();
bootstrapMap.put("spring.config.name", configName);
if (StringUtils.hasText(configLocation)) {
bootstrapMap.put("spring.config.location", configLocation);
}
//设置是否已经初始化BootStrap环境
bootstrapProperties.addFirst(
new MapPropertySource(BOOTSTRAP_PROPERTY_SOURCE_NAME, bootstrapMap));
for (PropertySource<?> source : environment.getPropertySources()) {
bootstrapProperties.addLast(source);
}
//......
//加载BootstrapConfiguration 配置类
List<String> names = SpringFactoriesLoader
.loadFactoryNames(BootstrapConfiguration.class, classLoader);
for (String name : StringUtils.commaDelimitedListToStringArray(
environment.getProperty("spring.cloud.bootstrap.sources", ""))) {
names.add(name);
}
//创建 Spring 容器
SpringApplicationBuilder builder = new SpringApplicationBuilder()
.profiles(environment.getActiveProfiles()).bannerMode(Mode.OFF)
.environment(bootstrapEnvironment)
.properties("spring.application.name:" + configName)
.registerShutdownHook(false)
.logStartupInfo(false)
.web(false);
List<Class<?>> sources = new ArrayList<>();
builder.sources(sources.toArray(new Class[sources.size()]));
AnnotationAwareOrderComparator.sort(sources);
final ConfigurableApplicationContext context = builder.run();
//创建祖先容器
addAncestorInitializer(application, context);
bootstrapProperties.remove(BOOTSTRAP_PROPERTY_SOURCE_NAME);
mergeDefaultProperties(environment.getPropertySources(), bootstrapProperties);
return context;
}
首先,SpringBoot项目是通过SpringApplicationBuilder启动,在上述逻辑中又构建了一个SpringApplicationBuilder对象,再次执行run方法:final ConfigurableApplicationContext context = builder.run();
所以启动流程会执行两遍,只是读取的配置文件和配置类不同。
同样的,当第二次创建SpringApplicationBuilder并启动时,会不会再次出发监听器,然后接着创建SpringApplicationBuilder呢?
肯定不会。否则就是死循环了。上面已经提到了,SpringCloud通过标识符BOOTSTRAP_PROPERTY_SOURCE_NAME来判断。监听器执行之后,会设置该变量对应值,下次启动前如果有值,表明已经执行。
上面有一行关键的代码:addAncestorInitializer(application, context);
ancestor 祖先的意思,来看一下:
private void addAncestorInitializer(SpringApplication application,
ConfigurableApplicationContext context) {
boolean installed = false;
//遍历所有的initializer,判断是否已经存在 祖先initializer
for (ApplicationContextInitializer<?> initializer : application
.getInitializers()) {
if (initializer instanceof AncestorInitializer) {
installed = true;
// 如果存在,则设置 bootStrapApplication
((AncestorInitializer) initializer)