先说结论,bootstrap配置文件中的与nacos服务相关的配置不会被application配置文件覆盖
但是如果想要在bootstrap中配置项目应用所需的属性,那么优先级低于application配置文件。会被覆盖
这也符合bootstrap本身是用于引导的作用。
至于nacos服务器上的配置,默认最大(可以配置修改),即使是命令参数也无法覆盖。
springBoot启动时当执行到prepareContext触发environmentPrepared事件,会触发BootstrapApplicationListener#onApplicationEvent
然后进入org.springframework.cloud.bootstrap.BootstrapApplicationListener#bootstrapServiceContext
StandardEnvironment bootstrapEnvironment = new StandardEnvironment();
//将默认Environment的PropertySource全部清空
MutablePropertySources bootstrapProperties = bootstrapEnvironment
.getPropertySources();
for (PropertySource<?> source : bootstrapProperties) {
bootstrapProperties.remove(source.getName());
}
String configLocation = environment
.resolvePlaceholders("${spring.cloud.bootstrap.location:}");
Map<String, Object> bootstrapMap = new HashMap<>();
//将配置文件名指定为bootstrap
bootstrapMap.put("spring.config.name", configName);
····省略代码···
SpringApplicationBuilder builder = new SpringApplicationBuilder()
.profiles(environment.getActiveProfiles())
//不打印启动banner
.bannerMode(Mode.OFF)
//设置新创建的Environment
.environment(bootstrapEnvironment)
// Don't use the default properties in this builder
.registerShutdownHook(false).logStartupInfo(false)
.web(WebApplicationType.NONE);
//设置primarySource为 BootstrapImportSelectorConfiguration
builder.sources(BootstrapImportSelectorConfiguration.class);
//又新创建了一个springCloud的springAplication
final SpringApplication builderApplication = builder.application();
//启动这个新创建的applicationContext
ConfigurableApplicationContext context = builder.run();
//设置祖先Initializer 后续执行时会把新创建的applicationContext中的配置加入到实际的applicationContext
addAncestorInitializer(application, context);
通过上面代码分析BootstrapApplicationListener新创建的springCloud的springApplication与主应用程序启动创建的application的
区别:
1、primarySource不同,扫描的basePackage不同
2、不会打印banner
3、加载的配置文件名称不同,新创建的加载配置文件名为bootstrap
相同点:
和启动时创建的springBoot相同,首先也从classpath/META-INF/spring.facotries文件中加载并实例化initializers和listeners,会在prepareContext时通过执行这些initializer操作applicationContext
新创建的springApplication也会在ConfigFileApplicationListener中加载bootstrap中的配置文件并且围绕BootstrapImportSelectorConfiguration进行bean的加载
创建的applicationContext会加载bootstrap.*配置文件,并且会合并到当前的applicationContext上的
environment中,通过addLast方法加到environment中MutablePropertySources的最后一个
通过BootstrapApplicationListener创建的springCloud的springApplication中创建的applicationContext即bean工厂,,经过ApplicationContextInitializer处理,会成为主应用程序启动创建的application的parent,在尝试从bean工厂中获取bean时,如本工厂不存在,从parent工厂中获取。
注意apply方法
private void apply(ConfigurableApplicationContext context,
SpringApplication application, ConfigurableEnvironment environment) {
@SuppressWarnings("rawtypes")
List<ApplicationContextInitializer> initializers = getOrderedBeansOfType(context,
ApplicationContextInitializer.class);
application.addInitializers(initializers
.toArray(new ApplicationContextInitializer[initializers.size()]));
addBootstrapDecryptInitializer(application);
}
会将新创建出来的applicationContext中的ApplicationContextInitializer全部放到application中,后续执行
applyInitializers将会执行父容器的Initializer进行配置的拉取
springBoot启动时当执行到prepareContext触发environmentPrepared事件,也会触发ConfigFileApplicationListener,优先级比BootstrapApplicationListener要低,也就是要晚于BootstrapApplicationListener执行,因此bootstrap优先于application.xml加载(注意,先加载未必优先级更高)
加载完毕调用addLast方法,放到environment。
void load() {
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();
while (!this.profiles.isEmpty()) {
Profile profile = this.profiles.poll();
if (isDefaultProfile(profile)) {
addProfileToEnvironment(profile.getName());
}
load(profile, this::getPositiveProfileFilter,
addToLoaded(MutablePropertySources::addLast, false));
this.processedProfiles.add(profile);
}
load(null, this::getNegativeProfileFilter, addToLoaded(MutablePropertySources::addFirst, true));
addLoadedPropertySources();
applyActiveProfiles(defaultProperties);
});
}
addLoadedPropertySources();会将配加载到的配置内用通过addLast方法放到 environment中MutablePropertySources的最后一个
因为ConfigFileApplicationListener要比BootstrapApplicationListener优先级低,因此此时application配置文件要在bootstrap配置之后(越靠前优先级越高)
在org.springframework.cloud.bootstrap.BootstrapApplicationListener#bootstrapServiceContext中
//设置祖先Initializer 后续执行时会把新创建的applicationContext中的配置加入到实际的applicationContext
addAncestorInitializer(application, context);
添加了一个AncestorInitializer
那么在prepareContext中执行applyInitializers(context);
会进入
org.springframework.cloud.bootstrap.BootstrapApplicationListener.AncestorInitializer#initialize
@Override
public void initialize(ConfigurableApplicationContext context) {
while (context.getParent() != null && context.getParent() != context) {
context = (ConfigurableApplicationContext) context.getParent();
}
//把父applicationContext中的配置加入到实际的applicationContext
reorderSources(context.getEnvironment());
//执行父applicationContext的initialize
new ParentContextApplicationContextInitializer(this.parent)
.initialize(context);
}
/**
* The name of the default properties.
*/
public static final String DEFAULT_PROPERTIES = "springCloudDefaultProperties";
private void reorderSources(ConfigurableEnvironment environment) {
PropertySource<?> removed = environment.getPropertySources()
.remove(DEFAULT_PROPERTIES);
if (removed instanceof ExtendedDefaultPropertySource) {
ExtendedDefaultPropertySource defaultProperties = (ExtendedDefaultPropertySource) removed;
environment.getPropertySources().addLast(new MapPropertySource(
DEFAULT_PROPERTIES, defaultProperties.getSource()));
for (PropertySource<?> source : defaultProperties.getPropertySources()
.getPropertySources()) {
if (!environment.getPropertySources().contains(source.getName())) {
environment.getPropertySources().addBefore(DEFAULT_PROPERTIES,
source);
}
}
}
}
}
这里会将父容器加载的配置文件springCloudDefaultProperties挨个拆出来放到当前environment MutablePropertySources的尾部
那么此时bootstrap配置文件会放置到application后面
PropertySourceBootstrapConfiguration继承ApplicationContextInitializer接口
注意这个PropertySourceBootstrapConfiguration是父容器创建的!
那么在prepareContext中执行applyInitializers(context)也会执行PropertySourceBootstrapConfiguration
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
CompositePropertySource composite = new OriginTrackedCompositePropertySource(
BOOTSTRAP_PROPERTY_SOURCE_NAME);
AnnotationAwareOrderComparator.sort(this.propertySourceLocators);
boolean empty = true;
ConfigurableEnvironment environment = applicationContext.getEnvironment();
for (PropertySourceLocator locator : this.propertySourceLocators) {
PropertySource<?> source = null;
source = locator.locate(environment);
if (source == null) {
continue;
}
logger.info("Located property source: " + source);
composite.addPropertySource(source);
empty = false;
}
if (!empty) {
MutablePropertySources propertySources = environment.getPropertySources();
String logConfig = environment.resolvePlaceholders("${logging.config:}");
LogFile logFile = LogFile.get(environment);
if (propertySources.contains(BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
propertySources.remove(BOOTSTRAP_PROPERTY_SOURCE_NAME);
}
insertPropertySources(propertySources, composite);
reinitializeLoggingSystem(environment, logConfig, logFile);
setLogLevels(applicationContext, environment);
handleIncludedProfiles(environment);
}
}
source = locator.locate(environment);
会调用nacos服务获取配置信息
那么locate从哪里来呢
@Autowired(required = false)
private List<PropertySourceLocator> propertySourceLocators = new ArrayList<>();
也就是从bean容器中获取,但是当前的applicationContext并没有创建这个bean
看下doGetBean中的一段逻辑
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// Not found -> check parent.
String nameToLookup = originalBeanName(name);
if (parentBeanFactory instanceof AbstractBeanFactory) {
return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
nameToLookup, requiredType, args, typeCheckOnly);
}
else if (args != null) {
// Delegation to parent with explicit args.
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else if (requiredType != null) {
// No args -> delegate to standard getBean method.
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
else {
return (T) parentBeanFactory.getBean(nameToLookup);
}
}
由于PropertySourceBootstrapConfiguration是父容器创建的,生效的nacos服务器配置自然是bootstrap配置。
private void insertPropertySources(MutablePropertySources propertySources,
CompositePropertySource composite) {
MutablePropertySources incoming = new MutablePropertySources();
incoming.addFirst(composite);
PropertySourceBootstrapProperties remoteProperties = new PropertySourceBootstrapProperties();
Binder.get(environment(incoming)).bind("spring.cloud.config",
Bindable.ofInstance(remoteProperties));
if (!remoteProperties.isAllowOverride() || (!remoteProperties.isOverrideNone()
&& remoteProperties.isOverrideSystemProperties())) {
propertySources.addFirst(composite);
return;
}
if (remoteProperties.isOverrideNone()) {
propertySources.addLast(composite);
return;
}
if (propertySources
.contains(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME)) {
if (!remoteProperties.isOverrideSystemProperties()) {
propertySources.addAfter(
StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME,
composite);
}
else {
propertySources.addBefore(
StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME,
composite);
}
}
else {
propertySources.addLast(composite);
}
}
根据PropertySourceBootstrapProperties判断nacos服务器上配置优先级,默认是addLast也就是放到优先级最高的位置。