• application.properties和bootstrap.properties的加载时机


    目录

    父容器加载BootstrapApplicationListener

    配置文件加载ConfigFileApplicationListener

    配置类加载BootstrapImportSelectorConfiguration


    看默认情况,不论使用参数更改的情况(spring.config.name)

    纯粹的SpringBoot主要就是自动配置,加载application.propertiesspring.factories中key为EnableAutoConfiguration的配置类进行bean的自动装配,并不涉及bootstrapbootstrap配置文件是SpringCloud扩展的配置文件,优先于application.properties加载,应用场景例如nacos配置中心的配置属性须在bootstrap.properties中配置

    下边来看源码是怎么优先加载的

    跟随启动类到run方法org.springframework.boot.SpringApplication#run,在prepareEnvironment方法中构建环境变量对象和加载配置文件

     org.springframework.boot.SpringApplication#prepareEnvironment

    创建Environment对象,通过各种监听器来加载不同的配置变量

    监听器列表debug如下,第四位ConfigFile监听器即就是加载配置文件(不论是bootstrap还是application都是由它加载)的监听器,优先级最高的Bootstrap监听器才是本次的重点

    父容器加载BootstrapApplicationListener

    org.springframework.cloud.bootstrap.BootstrapApplicationListener

    只处理EnvironmentPrepare事件 

    创建了一个属于bootstrap的新容器

    1. public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
    2. ConfigurableEnvironment environment = event.getEnvironment();
    3. // 是否开启bootstrap配置,默认开启
    4. if (!environment.getProperty("spring.cloud.bootstrap.enabled", Boolean.class,
    5. true)) {
    6. return;
    7. }
    8. // 标志位,如果当前环境变量有bootstrap标识,则跳过本类处理,这个标志位往下看就行
    9. // BOOTSTRAP_PROPERTY_SOURCE_NAME = bootstrap
    10. if (environment.getPropertySources().contains(BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
    11. return;
    12. }
    13. ConfigurableApplicationContext context = null;
    14. String configName = environment
    15. .resolvePlaceholders("${spring.cloud.bootstrap.name:bootstrap}");
    16. // 省略我不懂的代码
    17. // 。。。。
    18. if (context == null) {
    19. // 新创建一个bootstrap的容器
    20. context = bootstrapServiceContext(environment, event.getSpringApplication(),
    21. configName);
    22. event.getSpringApplication()
    23. .addListeners(new CloseContextOnFailureApplicationListener(context));
    24. }
    25. apply(context, event.getSpringApplication(), environment);
    26. }

    org.springframework.cloud.bootstrap.BootstrapApplicationListener#bootstrapServiceContext

    其中新创建了一个SpringApplication,设置配置文件名为bootstrap,又引入了BootstrapImportSelectorConfiguration配置类,最后执行run方法

    1. private ConfigurableApplicationContext bootstrapServiceContext(
    2. ConfigurableEnvironment environment, final SpringApplication application,
    3. String configName) {
    4. StandardEnvironment bootstrapEnvironment = new StandardEnvironment();
    5. MutablePropertySources bootstrapProperties = bootstrapEnvironment
    6. .getPropertySources();
    7. for (PropertySource source : bootstrapProperties) {
    8. bootstrapProperties.remove(source.getName());
    9. }
    10. // 设置容器的环境
    11. // 配置文件名为bootstrap\配置文件地址
    12. String configLocation = environment
    13. .resolvePlaceholders("${spring.cloud.bootstrap.location:}");
    14. String configAdditionalLocation = environment
    15. .resolvePlaceholders("${spring.cloud.bootstrap.additional-location:}");
    16. Map bootstrapMap = new HashMap<>();
    17. bootstrapMap.put("spring.config.name", configName);//配置文件名,默认bootstrap
    18. bootstrapMap.put("spring.main.web-application-type", "none");
    19. if (StringUtils.hasText(configLocation)) {
    20. bootstrapMap.put("spring.config.location", configLocation);
    21. }
    22. if (StringUtils.hasText(configAdditionalLocation)) {
    23. bootstrapMap.put("spring.config.additional-location",
    24. configAdditionalLocation);
    25. }
    26. // BOOTSTRAP_PROPERTY_SOURCE_NAME = bootstrap
    27. // 也是上个方法的标志位
    28. bootstrapProperties.addFirst(
    29. new MapPropertySource(BOOTSTRAP_PROPERTY_SOURCE_NAME, bootstrapMap));
    30. for (PropertySource source : environment.getPropertySources()) {
    31. if (source instanceof StubPropertySource) {
    32. continue;
    33. }
    34. bootstrapProperties.addLast(source);
    35. }
    36. // 这里重新使用SpringAoolication的建造器重新构建了一个boot容器
    37. // TODO: is it possible or sensible to share a ResourceLoader?
    38. SpringApplicationBuilder builder = new SpringApplicationBuilder()
    39. .profiles(environment.getActiveProfiles()).bannerMode(Mode.OFF)
    40. .environment(bootstrapEnvironment)
    41. // Don't use the default properties in this builder
    42. .registerShutdownHook(false).logStartupInfo(false)
    43. .web(WebApplicationType.NONE);
    44. final SpringApplication builderApplication = builder.application();
    45. if (builderApplication.getMainApplicationClass() == null) {
    46. builder.main(application.getMainApplicationClass());
    47. }
    48. if (environment.getPropertySources().contains("refreshArgs")) {
    49. builderApplication
    50. .setListeners(filterListeners(builderApplication.getListeners()));
    51. }
    52. // 为新容器引入BootstrapImportSelectorConfiguration配置类,执行run方法
    53. builder.sources(BootstrapImportSelectorConfiguration.class);
    54. final ConfigurableApplicationContext context = builder.run();
    55. // 设置容器id,父子关系,移除标志位
    56. context.setId("bootstrap");
    57. addAncestorInitializer(application, context);
    58. bootstrapProperties.remove(BOOTSTRAP_PROPERTY_SOURCE_NAME);
    59. mergeDefaultProperties(environment.getPropertySources(), bootstrapProperties);
    60. return context;
    61. }

    以上代码,知道了SpringCloud在启动时会新创建一个父容器来加一些Cloud需要的配置,接下来从配置文件加载和配置类加载来看父子容器的区别

    配置文件加载ConfigFileApplicationListener

    首先它处理两个事件,我们只关注环境加载事件

    跟代码

     

    这里在路径下,根据getSearchName返回的文件名进行加载文件 

     下边逻辑如果当前环境存在参数spring.config.name则使用参数的文件名进行加载,那刚才SpringCloud的新容器设置了参数=boostrap,所以在父容器中会加载bootstrap.properties文件,而在子容器中会使用默认的application.properties进行加载

    1. public static final String CONFIG_NAME_PROPERTY = "spring.config.name";
    2. private static final String DEFAULT_NAMES = "application";
    3. private Set getSearchNames() {
    4. if (this.environment.containsProperty(CONFIG_NAME_PROPERTY)) {
    5. String property = this.environment.getProperty(CONFIG_NAME_PROPERTY);
    6. Set names = asResolvedSet(property, null);
    7. names.forEach(this::assertValidConfigName);
    8. return names;
    9. }
    10. return asResolvedSet(ConfigFileApplicationListener.this.names, DEFAULT_NAMES);
    11. }

    配置类加载BootstrapImportSelectorConfiguration

    在启动类中会设置@EnableAutoConfiguration@ComponentScan来自动配置和扫描各种bean,而在父容器创建的时候只引入了BootstrapImportSelectorConfiguration

    EnableAutoConfiguration类似,引入了Selector来进行spring.factories文件配置类的加载

    但是与EnableAutoConfiguration不同的是Bootstrap的代码如下,查询的是spring.factories中key为BootstrapConfiguration的配置类

     这也是nacos配置中心得在bootstrap.properties中配置的原因,看代码如下

     nacos的配置属性类NacosConfigProperties

     自动加载前缀为spring.cloud.nacos.config的属性

     但是这个NacosConfigProperties类在初始化后会执行@PostConstruct方法

     如果bootstrap.properties环境变量未配置nacos配置中心地址,则会默认设置为localhost:8848

     

  • 相关阅读:
    Flutter:动画摘要
    ElementUI浅尝辄止29:Breadcrumb 面包屑
    游戏工作中用到的一些第3方软件和作用
    CV计算机视觉每日开源代码Paper with code速览-2023.10.23
    ABeam中国2022社招 | ABeam旗下德硕管理咨询(上海) 热招岗位虚位以待
    序列化技术ProtoBuf
    (c语言)typedef的用法
    信通传媒携各家虚商领袖,走进九牛汇
    SV(1)- 数据类型
    excel clear format
  • 原文地址:https://blog.csdn.net/xyjy11/article/details/126478865