1. Bootstrap配置文件在什么时候才会生效?
在SpringBoot 2.4.x
的版本之后,对于bootstrap.properties
/bootstrap.yaml
配置文件(我们合起来成为Bootstrap
配置文件)的支持,需要导入如下的依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
<version>3.1.0</version>
</dependency>
其实这个jar包里什么都没有,就只有一个标识类Marker
,用来标识要开启Bootstrap
配置文件的支持,至于原理是什么?我们下面逐步去进行解析
2. Bootstrap配置文件生效的原因
既然是SpringBoot
,我们主要研究它的run
方法,在这个方法中定义了SpringBoot
启动的整个流程。
我们要研究的是Bootstrap
配置文件的相关信息,就肯定和环境对象有关系,因此我们关注准备环境变量的步骤。
在这里我们可以看到,首先是创建了一个环境对象,接着,通知所有的监听器,告诉它们环境对象已经准备好了,可以开始去进行配置环境信息了。我们关心的application
配置文件以及bootstrap
配置文件,都是使用监听器的方式去监听环境准备好的事件时去扩展的。
对于Bootstrap
的配置文件,将会被BootstrapApplicationListener
去进行处理,而这个监听器,在cloud-context
的jar包下通过SpringFactories
的方式去进行了配置,因此在运行时,就会实例化这个监听器并在创建环境之后去进行回调。
而对于application
配置文件,是由另外一个监听器(EnvironmentPostProcessorApplicationListener
)去进行处理的,这个监听器里面会读取spring.config.name
作为配置文件名,如果没有的话,会使用application
作为默认的配置文件名。
既然它是一个监听器,那么,我们需要关注的是它的onApplicationEvent
方法。
首先我们需要注意一个点,这个监听器的Order很高,一般来说,它都是第一个被回调到的监听器,因此在来到这里时,其实application
配置文件并未加载到环境当中,但是命令行参数是已经被解析到环境当中了,我们上面的步骤当中已经提到过。
我们首先要关注的一个点就是bootstrapEnabled(environment)
,很明显,它是用来判断bootstrap是否要生效的。我们打开它的源码
我们可以看到,是否开启bootstrap
,只需要满足当前环境信息当中有配置spring.cloud.bootstrap.enabled=true
,或者当前的依赖当中存在有org.springframework.cloud.bootstrap.marker.Marker
这个类,就会开启bootstrap
。
而我们上面已经说过了,导入spring-cloud-starter-bootstrap
这个组件的作用就是为了让容器中存在有该标识类,我们下面点开源码来验证一下。
我们可以看到,确实只有一个Marker
的标识类,其它就什么都没有了。
实际上,通过在启动参数中配置相关的属性也可以(因为Bootstrap
的监听器优先级比较高,因此配置在application
配置文件当中无效,application
配置文件还未加载到环境当中来,所以必须在启动参数中去进行配置),不用非得导入jar包(前提容器中已经有了cloud-context
的jar包,因为这个jar包当中导入了BootstrapApplicationListener
组件)。
3. Bootstrap的核心代码-bootstrapServiceContext
我们继续来看BootstrapApplicationListener
的后续源码中的bootstrapServiceContext
方法,这个方法是整个Bootstrap
的核心。
3.1 Bootstrap隔离容器的构建
image.png image.png我们可以看到,其实这里的主要逻辑,就是去创建一个隔离容器,这个容器中的组件是由BootstrapImportSelectorConfiguration
它去进行处理的。还有一个关键点,它往自己构建的一个Map的属性源当中加入了一个配置项spring.config.name
为bootstrap
(如果没有自己额外配置的话),这个配置项,决定的是它在运行时需要加载的配置文件的名称。
而这里其实会运行bootstrap隔离容器,因此也会有环境的监听器EnvironmentPostProcessorApplicationListener
,这个组件就会读取到我们配置的spring.config.name
配置项,去加载bootstrap
配置文件。
其实它就是导入一个Selector
,通过Selector
给容器中导入组件。
而这个Selector
,其实就是加载BootstrapConfiguration
中配置的相关配置。
那么BootstrapConfiguration
配置了什么?下面是cloud-context
中配置的自动配置的组件。
通过自动配置的组件,我们可以看到其中一个肯定是关于处理占位符的组件,它主要给容器中导入一个嵌入式的值解析器,用来解析占位符。
3.2 BootstrapConfiguration
之PropertySourceBootstrapConfiguration
这个组件值得我们去进行关注
image.png我们可以看到,它本身是一个ApplicationContext
的初始化器,并且会自动注入Bootstrap
隔离容器中配置的所有的Locator
,这个Locator
是实现分布式配置中心的关键。
下面我们来看Nacos-Config
的源码,看看它是怎么加载ConfigServer
中的配置文件的?
我们可以看到,它给Bootstrap
隔离容器中导入了一个NacosConfigBootstrapConfiguration
组件,我们来看这个组件的源码
我们可以看到,它给容器中导入了一个NacosConfigManager
组件,包装一个ConfigService
,这个组件是Nacos
中用来读取远程ConfigServer中的配置信息的组件。如果使用过Nacos的API的使用的话,应该比较熟悉这个组件。
它还给容器中导入了一个Locator
组件,而导入的这个Locator
组件的locate
方法的代码如下,我们可以很明确的看到了准备dataId
,并且开始去加载配置文件,并将所有要加载的配置文件进行聚合成为一个属性源对象,并返回,Spring
就会自己对其去进行处理,并导入到app容器的环境对象当中去。
我们可以知道,所有的Locator
组件,都会被注入到PropertySourceBootstrapConfiguration
这个组件中,但是这个组件并不会在Bootstrap
隔离容器中生效,这个组件,实际上会在app
容器中去生效,我们来找它的源码。
我们在BootstrapApplicationListener
这个组件的onApplicationEvent
方法中,可以看到有apply
这个步骤。这个步骤的源码如下:
我们可以看到,它将app容器的组件和bootstrap
隔离容器中的组件去进行了合并,并设置到app
容器所对应的SpringApplication
组件当中。
问题来了,ApplicationContext
的初始化器在什么时候会被应用?
3.3 ApplicationContext
的初始化器什么使用生效
我们回到SpringApplication
的run
方法,找到准备Context的一个步骤。
我们可以看到,在这个方法中存在有applyInitializers
这样的步骤,那就很明显了,就是在这里回调的。在创建完ApplicationContext
之后,立马就回调所有的初始化器,其实根据初始化器这个名词,我们也可以大概猜到,它会在创建完ApplicationContext
之后立马就会去进行回调。
网友评论