使用Nacos bootstrap application 远程配置优先级

结论

先说结论,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<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进行配置的拉取

加载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中的配置加入到实际的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后面

加载nacos服务器上配置

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

你可能感兴趣的:(nacos,java,spring,boot,nacos)