一、前言
关于springboot中bootstrap和application的区别,网上很多文章可能讲述的并不清晰,尤其是根据网上的讲解对比自己的测试,出入很大,本质原因都是理解不深刻和表述不清楚所致。
因此花了一些时间,通过debug源码方式,来深入分析两者的不同。
二、效果演示
bootstrap和application的区别主要集中在加载优先级,以及对bootstrap中的一些属性配置是否可以被application覆盖的问题。
properties配置文件为例(properties和yml等优先级本篇不做分析),创建bootstrap.properties和applicatoin.properties
在bootstrap.properties中仅配置端口号为
server.port=8081
在application.properties中仅配置端口号为:
server.port=8082
在bootstrap.properties和application.properties同时存在时,启动控制台,查看当前端口:
通过后台日志看到,端口号为8082,以applicaiton.properties中的配置为准,也就是application.properties覆盖了bootstrap.properties中的配置,并且生效。
三、修改配置属性
在bootstrap.properties中修改配置,新增spring.profiles.include属性
server.port=8081
spring.profiles.include=test
application.properties同理
server.port=8082
spring.profiles.include=prod
spring.profiles.include的作用:
例如项目分为开发、测试、生产三个不同阶段(环境),每个阶段都会有db、redis等的不同配置信息,例如application-test.properties、application-prod.properties等。
application-test.properties文件配置:
server.port=8083
application-prod.properties文件配置:
server.port=8080
根据第二步的加载逻辑,application.properties(端口8082)会覆盖bootstrap.properties(端口8081)中的配置,在applicaton.properties中引入了prod配置项,也就是最终应该以application-prod.properties文件中的端口8080为启动端口,运行项目:
启动端口居然为 8083
也就是实际生效的配置文件为application-test.properties。
四、源码分析
对与三和四的不同效果,也是网上困惑最多的点,我们通过阅读源码的方式,梳理一下加载流程。
1、特殊属性
打开ConfigFileApplicationListener.java(源码在spring-boot-2.3.7.RELEASE.jar)
static {
Set<String> filteredProperties = new HashSet<>();
filteredProperties.add("spring.profiles.active");
filteredProperties.add("spring.profiles.include");
LOAD_FILTERED_PROPERTY = Collections.unmodifiableSet(filteredProperties);
}
/**
* The "active profiles" property name.
*/
public static final String ACTIVE_PROFILES_PROPERTY = "spring.profiles.active";
/**
* The "includes profiles" property name.
*/
public static final String INCLUDE_PROFILES_PROPERTY = "spring.profiles.include";
/**
* The "config name" property name.
*/
public static final String CONFIG_NAME_PROPERTY = "spring.config.name";
/**
* The "config location" property name.
*/
public static final String CONFIG_LOCATION_PROPERTY = "spring.config.location";
/**
* The "config additional location" property name.
*/
public static final String CONFIG_ADDITIONAL_LOCATION_PROPERTY = "spring.config.additional-location";
上属性在bootstrap的配置项有优先加载,而且不会被application配置文件覆盖
2、bootstrap优先加载
为什么bootstrap中的属性会优先加载
在启动springboot时,通过debug跟踪启动类的加载顺序可知,BootstrapApplicationListener的加载顺序非常靠前,并且代码中指定了配置文件的最先加载名称为bootstrap(在springboot中文件名和文件后缀分开加载) 。
3、固有属性加载
优先加载ACTIVE_PROFILES_PROPERTY和INCLUDE_PROFILES_PROPERTY
/**
* The "active profiles" property name.
*/
public static final String ACTIVE_PROFILES_PROPERTY = "spring.profiles.active";
/**
* The "includes profiles" property name.
*/
public static final String INCLUDE_PROFILES_PROPERTY = "spring.profiles.include";
bootstrap.properties中配置的spring.profiles.include为test,上图debug中也优先加载test
3、加载application
继续debug,application配置文件中指定的prod属性也开始被加载
其中test为existingProfiles,也就是已经加载的属性
prod为includeProfiles,是当前刚刚获取的属性。
bootstrap和application中的属性配置已完成加载。
4、配置解析
配置加载后,开始去读,其中profiles为一个队列,列表中加载了prod和test两个属性,最后读取的最先处理,列表中prod排在最前,先poll(),如下图:
同理,test最后加载,以application-test.properties配置文件为准,端口为8083。
5、结果分析
通过以上分析可以清楚了解:
优先级:bootstrap优先执行,application在bootstrap加载后再执行
作用域:bootstrap作为系统环境配置项,application作为服务应用配置,可以简单将bootstrap比喻为虚拟机,application为虚拟机中运行的程序。
属性配置:一般和应用相关的在application中配置,和系统环境相关或者固定基本不会改动的参数在bootstrap中配置。
注意事项:即便了解了上述过程,配置时,尽量避免bootstrap和application中配置相同属性。