• 使用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);
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    通过上面代码分析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);
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    会将新创建出来的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);
    					});
    		}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    addLoadedPropertySources();会将配加载到的配置内用通过addLast方法放到 environment中MutablePropertySources的最后一个
    因为ConfigFileApplicationListener要比BootstrapApplicationListener优先级低,因此此时application配置文件要在bootstrap配置之后(越靠前优先级越高)

    处理父容器配置文件

    在org.springframework.cloud.bootstrap.BootstrapApplicationListener#bootstrapServiceContext中

      //设置祖先Initializer 后续执行时会把新创建的applicationContext中的配置加入到实际的applicationContext
            addAncestorInitializer(application, context);
    
    • 1
    • 2

    添加了一个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);
    		}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    /**
    * The name of the default properties.
    */
    public static final String DEFAULT_PROPERTIES = "springCloudDefaultProperties";
    
    • 1
    • 2
    • 3
    • 4
    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);
    					}
    				}
    			}
    		}
    
    	}
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    这里会将父容器加载的配置文件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);
    		}
    	}
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    source = locator.locate(environment);
    
    • 1

    会调用nacos服务获取配置信息
    那么locate从哪里来呢

     @Autowired(required = false)
    	private List<PropertySourceLocator> propertySourceLocators = new ArrayList<>();
    
    • 1
    • 2

    也就是从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);
    				}
    			}
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    由于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);
    		}
    	}
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    根据PropertySourceBootstrapProperties判断nacos服务器上配置优先级,默认是addLast也就是放到优先级最高的位置。

  • 相关阅读:
    Redis 数据结构
    LabVIEW基础-子VI创建与调用
    Ubtunu排查磁盘空间是否已满—并清理的方式
    Python 3.11 版本是对线程安全做了什么更改吗
    @ControllerAdvice
    没想到吧,Spring中还有一招集合注入的写法
    机器学习(11)---降维PCA
    pytorch的gpu版本安装以及cpu版本的卸载
    9种js数组去重方法都有哪些?
    buu web部分wp
  • 原文地址:https://blog.csdn.net/qq_37436172/article/details/128054448