springboot配置读取顺序

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

本文主要阐述一下微服务结合配置中心、启动命令入参、本地文件都存在的情况的一个读取顺序及相同属性的赋值问题。主要用于记录自己学习情况,大神勿喷,谢谢

一、项目准备

1.创建一个springboot项目,接入nacos注册中心和配置中心。
2.在项目根目录下创建application.yml和config/application.yml,在项目目录resources下创建application.yml、config/application.yml、bootstrap.properties文件在各个文件中添加不同的配置属性
3.在各个配置文件中添加同一个属性,赋不同的值。
4.将配置中心配置好,并在配置中心添加两个属性,一个不同于其他的值,一个和步骤3的属性一致,但值不同。
5.在idea中配置启动参数操作同配置中心。
步骤1、2、3验证各个本地配置文件的读取情况以及取值顺序
步骤4验证在有配置中心的情况下读取和取值顺序
步骤5验证在启动命令传参的情况下读取和取值顺序

二、验证

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

@RestController
public class UserController {
    @Value("${com.jy}")
    private String path;
    @Value("${com.jy.test}")
    private String test;
    @Value("${file.config.application.demo}")
    private String fileConfigApplicationDemo;
    @Value("${file.application.demo}")
    private String fileApplicationDemo;
    @Value("${config.application.demo}")
    private String classpathConfigApplicationDemo;
    @Value("${application.demo}")
    private String classpathApplicationDemo;
    @Value("${bootstrap.demo}")
    private String classpathBootstrap;
    @Value("${com.test.demo}")
    private String comTestDemo;
    @Value("${com.nacos.demo}")
    private String comNacosDemo;
    @RequestMapping("/test")
    public String getNameById(String id){
        System.out.println("测试接口入参,id="+id);
        System.out.println("测试文件读取顺序--所有配置相同属性的值:"+path);
        System.out.println("测试文件读取顺序--不同属性之根目录config下的值:"+fileConfigApplicationDemo);
        System.out.println("测试文件读取顺序--不同属性之根目录application文件的值:"+fileApplicationDemo);
        System.out.println("测试文件读取顺序--不同属性之项目目录config下的值:"+classpathConfigApplicationDemo);
        System.out.println("测试文件读取顺序--不同属性之项目目录application文件的值:"+path);
        System.out.println("测试文件读取顺序--不同属性之项目目录bootstrap文件的值:"+classpathBootstrap);
        System.out.println("测试文件读取顺序--不同属性之启动命令传参的值:"+comTestDemo);
        System.out.println("测试文件读取顺序--不同属性之nacos配置中心的值:"+comNacosDemo);
        System.out.println("测试nacos:"+test);
        return path;
    }
}

到此项目和所需的配置我们已经准备好了,下面就运行项目看看配置文件是否能正常读取
在这里插入图片描述

由此可见我们添加的配置都读取到了,说明每个配置文件和启动传参以及配置中心的配置都是正常读取的。而相同属性的com.jy的值从结果来看读取到的是nacos配置中心的值nacos-application-properties,说明配置中心的优先级是最高的。那么读取顺序又是什么呢?

三、探究原理

1.读取顺序

我们先把结果梳理一下:
本地文件:
1.在有springcloud的情况下,首先读取的bootstrap命名的文件,然后读取application命名的文件,相同属性的值,application文件的值会覆盖bootstrap文件的值。
2.前缀相同的情况,首先读取的根目录config文件夹下的配置文件–>根目录下的文件–>项目目录resources目录下config文件夹下的配置文件–>项目目录下的配置文件,按此顺序读取配置。
3.相同路径相同前缀的情况,配置文件按后缀读取顺序properties–>xml–>yml–>yaml
以上操作是对文件的解析,解析后封装成PropertySource下各个子类型的对象存放在环境变量中。bootstrap和application文件对应的封装对象是OriginTrackedMapPropertySource,application对应的封装对象会排在bootstrap前面,在@Value赋值的时候通过PropertySourcesPropertyResolver的getProperty(String key)获取值,一旦获取到后面的就不会再重新赋值。
4.加入nacos配置中心的配置信息最后封装成BootstrapPropertySource类型的对象排序在最前面,最先获取。
5.启动命令的传参封装成SimpleCommandLinePropertySource类型,介于BootstrapPropertySource和OriginTrackedMapPropertySource之间,也就是说,配置中心没有的话就查看这个对象里的信息,然后才是本地配置文件的。
在这里插入图片描述
在这里插入图片描述

但是nacos配置中心的本身的相关信息比如地址、dataid之类的信息,我们还是要配置在bootstrap文件中的,至于为什么,我们将在下面的源码梳理中说明

2.源码梳理

如果是springcloud项目下启动服务SpringApplication.run()会执行两边。这点有一些类似,创建A时发现B属性是个对象需要创建,于是就开始创建B对象,等B对象创建完了接着创建A对象。
下面我们就按照run方法里的执行顺序进行梳理:
1、首先解析到的启动命令的传参,将参数先封装成ApplicationArguments对象,但显然这个不我们想要的,因为所有的配置信息最终要封装成的都是PropertySource对象,所以后面必定有其他操作。
在这里插入图片描述
在封装对象后下一行就是准备环境的方法ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);进入该方法
在这里插入图片描述
在这里插入图片描述
到此命令传参的就解析完了。接着往下走就是监听器了,我们的本地配置文件都是通过监听器处理的。(BootstrapApplicationListener先执行、ConfigFileApplicationListener后执行。这两个是我们关注的)
进入到BootstrapApplicationListener中

public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
		ConfigurableEnvironment environment = event.getEnvironment();
		//判断是否为springcloud,我们的用例是所以此处取反后为false
		if (!environment.getProperty("spring.cloud.bootstrap.enabled", Boolean.class,
				true)) {
			return;
		}
		// don't listen to events in a bootstrap context
		//配置对象是否已经包含了bootstrap的配置,此处我们是第一次,所以是不包含的(第二次时就包含了所以return掉了)
		if (environment.getPropertySources().contains(BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
			return;
		}
		ConfigurableApplicationContext context = null;
		String configName = environment
				.resolvePlaceholders("${spring.cloud.bootstrap.name:bootstrap}");//configName  = bootstrap
		//此处没有通过if判断的跳过		
		for (ApplicationContextInitializer<?> initializer : event.getSpringApplication()
				.getInitializers()) {
			if (initializer instanceof ParentContextApplicationContextInitializer) {
				context = findBootstrapContext(
						(ParentContextApplicationContextInitializer) initializer,
						configName);
			}
		}
		//创建springcloud容器,在这里会执行final ConfigurableApplicationContext context = builder.run();这个就是我们本节开头说的情况。
		if (context == null) {
			context = bootstrapServiceContext(environment, event.getSpringApplication(),
					configName);
			event.getSpringApplication()
					.addListeners(new CloseContextOnFailureApplicationListener(context));
		}

		apply(context, event.getSpringApplication(), environment);
	}

上面是服务启动时进入BootstrapApplicationListener,然后发现有springcloud于是创建,然后又执行到run方法中,接着又来到BootstrapApplicationListener,但是在第二个if判断中就中断了。于是开始执行ConfigFileApplicationListener里的监听方法。通过重重调用来到本类中的添加方法

protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
		RandomValuePropertySource.addToEnvironment(environment);
		new Loader(environment, resourceLoader).load();
	}

在这里插入图片描述
第一次执行完该方法后配置对象会多一个OriginTrackedMapPropertySource类型的,是bootstrap文件封装成的
在这里插入图片描述
接下来就是准备容器prepareContext(),这时会处理我们的自动装配类了。而nacos就是在此时处理的,注意这会还是springcloud容器哦,所以前面我们说nacos的注册信息要放在bootstrap的文件中,不然此时怎么自动装配呢。但是此时还没有添加到配置对象的集合中,也就是还没有封装成BootstrapPropertySource。然后进过第二次ConfigFileApplicationListener的处理(流程和前面一样的)我们的配置文件集合已经有了命令启动的配置信息、本地配置文件的配置信息(但springboot容器的环境中还没有springcloud创建时的bootstrap封装的对象)。
在这里插入图片描述
所以后面还需要我们将nacos的配置信息封装添加到集合中,以及将springcloud封装的配置信息添加进去。这样我们的配置信息就算读取完成了。
第二次执行prepareContext()时会进入到PropertySourceBootstrapConfiguration中的initialize方法,该方法会将nacos的配置信息进行封装成BootstrapPropertySource类型对象然后添加到集合的最前面。在这之前BootstrapApplicationListener的initialize方法会将springcloud的配置信息添加到springboot的配置集合中。
好了,这下配置信息是都读取并封装完成了。

总结

本文只是介绍了一些我们常见的配置方式,还有其他的自定义文件等情况没有加入讨论,后期有时间再继续研究吧。文章写得比较粗略,请大家见谅!一起加油吧!!!

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值