1.配置加载现象及加载场景描述。假设有一个如下表所示的特殊场景的配置文件,配置文件是如何生效的,加载顺序是怎样的?同一个参数配置在多个地方,应用中哪个参数项生效呢?配置文件配置项application.yamlxxx:yyy:zzz:from-application.yamlapplication-dev.yamlxxx:yyy:zzz:from-application-dev.yamlbootstrap.yamlxxx:yyy:zzz:from-bootstrap.yamlconfig-serverxxx:yyy:zzz:from-config-order.yaml系统环境变量XXX_YYY_ZZZ=from-system-environmentjvm启动参数-Dxxx.yyy.zzz=from-jvm-args如果使用配置中心,需要添加spring-cloud配置中心配置。2、示例代码演示版本jdk1.8spring-boot2.6.2spring-cloud2021.0.01,建立配置中心config-server,先申请gitee,使演示配置中心可以从git获取配置项。(1)pom.xml如下:4.0.0org.springframework.bootspring-boot-starter-parent2.6.2com.mixfateconfig-server0.0.1-SNAPSHOTconfig-server1.82021.0.0org.springframework.cloudspring-cloud-config-serverorg.springframework.bootspring-boot-starter-test测试org.springframework.cloudspring-cloud-dependencies<版本>${spring-cloud.version}pomimportorg.springframework.bootspring-boot-maven-plugin(2)ConfigServerApplication.java如下:packagecom.mixfate;importorg.springframework.boot.SpringApplication;导入org.springframework.boot.autoconfigure.SpringBootApplication;导入org.springframework.cloud.config.server.EnableConfigServer;@EnableConfigServer@SpringBootApplication公共类ConfigServerApplication{publicstaticvoidmain(String[]args){SpringApplication.run(配置服务器,参数);}}(3)application.yaml如下:spring:cloud:config:server:git:uri:https://gitee.com/xxx/spring-cloud-configusername:xxxpassword:123456default-label:masterserver:port:8082logging:level:root:infoorg.springframework.cloud:debug需要在gitee上创建一个仓库spring-cloud-config(名字可以随意选择),default-label:master表示使用master分支2.创建一个demo工程config-order,演示配置文件的加载顺序。(1)pom.xml如下:4.0.0org.springframework.bootspring-boot-starter-parent2.6.2com.mixfateconfig-order0.0.1-SNAPSHOTconfig-order1.82021.0.0org.springframework.cloudspring-cloud-starter-configorg.springframework.cloudspring-cloud-starter-bootstraporg.springframework.bootspring-boot-starter-test测试org.springframework.cloudspring-cloud-dependencies<版本sion>${spring-cloud.version}pomimport<插件>org.springframework.bootspring-boot-maven-plugin(2)ConfigOrderApplication.java如下:包com.mixfate;导入org.springframework.beans.factory.annotation.Value;导入org.springframework.boot.ApplicationArguments;导入org.springframework.boot.ApplicationRunner;导入org.springframework.boot.SpringApplication;导入org.springframework.boot.autoconfigure.SpringBootApplication;importorg.springframework.stereotype.Component;@SpringBootApplication公共类ConfigOrderApapplication{publicstaticvoidmain(String[]args){SpringApplication.run(ConfigOrderApplication.class,args);}}@ComponentclassInitRunnerimplementsApplicationRunner{@Value("${xxx.yyy.zzz}")privateStringname;@Overridepublicvoidrun(ApplicationArgumentsargs)throwsException{System.out.println("xxx.yyy.zzzis"+name);}}使用InitRunner演示应用程序初始化时输出参数的值(3)yaml配置文件如下:bootstrap.yaml配置spring:cloud:config:uri:http://localhost:8082application:name:config-orderprofiles:active:devxxx:yyy:zzz:from-bootstrap.yamlapplication.yaml配置server:port:8085logging:level:root:infoxxx:yyy:zzz:from-application.yamlapplication-dev.yaml配置xxx:yyy:zzz:from-applicatoin-dev.yaml需要在gitee上的仓库spring-cloud-config中注册,需要在config-order.yaml中创建,与本应用同名即可启动配置中心后使用http://localhost:8082/config-order.yaml访问,查看配置信息。3.Demo案例配置环境好环境变量和jvm启动参数。运行程序,可以看到如下结果xxx.yyy.zzz是from-config-order.yaml,说明已经从配置中心获取了配置项。然后在配置中心更改配置项,说明配置中心没有对应的配置项,如下:config-order.yaml:xxx:yyy:zzz000:from-config-order.yaml再次运行程序即可看到结果xxx.yyy.zzz是from-jvm-args,说明是从jvm启动参数中获取的。如果去掉jvm参数,结果变成xxx.yyy.zzzisfrom-system-environment,说明是从系统环境变量中获取的。可以看到这个配置是顺序加载的,可以继续尝试修改其余配置文件中的参数查看结果。3、配置源码分析在idea中运行调试,一步步查看源码。从SpringApplication.run(ConfigOrderApplication.class,args)开始调试;并继续调试到SpringApplication类的方法ConfigurableApplicationContextrun(String...args)直到configureIgnoreBeanInfo(environment);这一行,查看变量环境,如下图:查看关键属性环境??中propertySources的propertySourceList,可以看到在这个List中初始化了9个propertysources,当然还有其他的,这里是一些关键的配置项.同时可以看到关键属性propertyResolver,即属性解析器。在上面截图的列表中看到配置文件的顺序是configurationProperties->systemProperties->systemEnvironment->...->bootstrap.yaml->...->appplication-dev.yaml->application.yaml:那么我们看一下这个PropertySourceList是如何组装的,追踪到方法ConfigurableEnvironmentprepareEnvironment,查看变量environment的值,如下图所示:可以看到getOrCreateEnvironment()方法创建后,有两个值systemProperties和systemEnvironment的顺序,然后继续configureEnvironment(environment,applicationArguments.getSourceArgs());检查此方法后的变量。根据变量名可以猜到应该属于args参数,所以观察配置参数如下,就是commandLineArgs,把它加到第一个位置。跟踪方法configurePropertySources可以看到sources.addFirst首先添加了它。接下来就是去ConfigurationPropertySources.attach(environment);,进入方法attach方法后,可以再跟踪一个sources.addFirst,放到第一个位置。然后运行??listeners.environmentPrepared(bootstrapContext,environment);之后可以观察到增加了几个配置,完成prepareEnvironment方法后基本准备工作完成,然后在关键方法prepareContext中完成初始化过程。最终结果如下:在BootstrapApplicationListener的方法initialize中调用reorderSources完成bootstrap.yaml配置,可以看到是addLast;而配置中心bootstrapProperties是在PropertySourceBootstrapConfiguration的方法initialize中调用insertPropertySources完成的,其中传入。添加第一(p);把配置中心放在前面。属性读取的过程。跟着上面的调试代码,大致了解了propertySourceList的组装过程。让我们看看PropertiesResolver是如何解析它的。另外写一段demo代码如下,跟踪其执行过程。一路调试到ConfigurationPropertySourcesPropertyResolver类中的方法findPropertyValue,再跟进attached.findConfigurationProperty(name),可以在方法中的for循环中getSource(),打开Evaluate回车,可以看到propertySourceList中getSource()就是上一步的propertySourceList,如下图:从这里可以看出重点,属性是从propertySourceList中一个一个解析出来的,如果之前已经获取到对应的值,则为配置的值later不会被解析,所以这是一个优先问题,不是不同配置文件之间参数覆盖的过程。我之前也注意到了一点。配置系统环境变量时,配置的环境变量参数为XXX_YYY_ZZZ,而不是xxx.yyy.zzz。为什么也能解析呢?这实际上是一种约定俗成的分析方法。同样的跟踪获取配置项的方法已经进入了SpringConfigurationPropertySource的方法getConfigurationProperty,如下:可以看到name=xxx.yyy.zzz,里面有一个mapper.map(name),注意看mapper就是SystemEnvironmentPropertyMapper,继续跟踪进去可以发现有一个转换过程convertName,如下图,将xxx.yyy.zzz转换成XXX_YYY_ZZZ,所以也是可以的。配置加载时间顺序的问题。可以看到正在加载配置文件。如果从配置中心获取配置,则需要启动prepareContext从配置中心读取配置项。如果在配置中心配置了不同的日志格式怎么办?新的日志格式加载到配置中心后是否必须开始使用?在实践中确实可以得到结果。例如配置中心定义日志格式为console:"%clr(%d{yyyy-MM-ddHH:mm:ss.SSS}){faint}%m%n",启动日志如下:可以看到一个控制台日志格式的变化过程。4.总结经过以上分析,我们可以得出常用配置文件中读取配置的结论:config配置中心=>jvm参数=>系统环境变量=>项目中的application-xxx.yaml=>application.yaml中项目=>bootstrap.yaml。spring-boot项目可以通过多种灵活的方式设置配置项。一般来说,项目中的application.yaml是一个默认的配置项,然后通过基础配置项给参数赋不同的值。