使用Nacos bootstrap application 远程配置优先级
迪丽瓦拉
2024-02-17 05:13:18
0

结论

先说结论,bootstrap配置文件中的与nacos服务相关的配置不会被application配置文件覆盖
但是如果想要在bootstrap中配置项目应用所需的属性,那么优先级低于application配置文件。会被覆盖
这也符合bootstrap本身是用于引导的作用。
至于nacos服务器上的配置,默认最大(可以配置修改),即使是命令参数也无法覆盖。

加载bootstrap配置文件

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 bootstrapMap = new HashMap<>();//将配置文件名指定为bootstrapbootstrapMap.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为 BootstrapImportSelectorConfigurationbuilder.sources(BootstrapImportSelectorConfiguration.class);//又新创建了一个springCloud的springAplication final SpringApplication builderApplication = builder.application();//启动这个新创建的applicationContextConfigurableApplicationContext context = builder.run();//设置祖先Initializer 后续执行时会把新创建的applicationContext中的配置加入到实际的applicationContextaddAncestorInitializer(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 initializers = getOrderedBeansOfType(context,ApplicationContextInitializer.class);application.addInitializers(initializers.toArray(new ApplicationContextInitializer[initializers.size()]));addBootstrapDecryptInitializer(application);}

会将新创建出来的applicationContext中的ApplicationContextInitializer全部放到application中,后续执行
applyInitializers将会执行父容器的Initializer进行配置的拉取

加载application配置文件

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中的配置加入到实际的applicationContextaddAncestorInitializer(application, context);

添加了一个AncestorInitializer
那么在prepareContext中执行applyInitializers(context);
会进入
org.springframework.cloud.bootstrap.BootstrapApplicationListener.AncestorInitializer#initialize

	@Overridepublic void initialize(ConfigurableApplicationContext context) {while (context.getParent() != null && context.getParent() != context) {context = (ConfigurableApplicationContext) context.getParent();}  //把父applicationContext中的配置加入到实际的applicationContextreorderSources(context.getEnvironment());//执行父applicationContext的initializenew 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后面

加载nacos服务器上配置

PropertySourceBootstrapConfiguration继承ApplicationContextInitializer接口
注意这个PropertySourceBootstrapConfiguration是父容器创建的!
那么在prepareContext中执行applyInitializers(context)也会执行PropertySourceBootstrapConfiguration

	@Overridepublic 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 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也就是放到优先级最高的位置。

相关内容