一,加载自定义配置文件
springboot启动时会默认加载application.properties和application.yml以及application-{profile}.properties和application-{profile}.yml
profile为对应的环境变量,比如dev,所以一般的资源文件结构如下:

如果配置内容太多的话,就会显得很不方便,解决方案一般会分成多个不同的配置文件,期望加载application.yml后,会加载对应profile文件夹下的所有配置文件。
分多个配置文件的接口如下:

所以下面开始工作吧(大概思路是,利用EnvironmentPostProcessor接口,读取自定义文件夹,把内容加载到environment里去)
1,新建实现实现EnvironmentPostProcessor的类O2oClientEnvironmentPostProcessor
2,利用PathMatchingResourcePatternResolver加载多个文件
3,PropertySourceLoader读取相应的配置文件,这里的location可以自己定义,本文为classpath:{profile}/client-*.fileExtension,如果profile为dev,PropertySourceLoader为YamlPropertySourceLoader的话,则为classpath:dev/client-*.yml,就会加载dev文件夹下以client-开头的所有yml文件。
4,添加进environment。
内容如下:

类写好了,下面让它起作用,在META-INF/spring.factories下面配置
org.springframework.boot.env.EnvironmentPostProcessor=com.mama100.o2o.client.config.O2oClientEnvironmentPostProcessor
然后就OK了,可以直接注入自定义文件的属性。
二,官网的说明
官网文档关于EnvironmentPostProcessor的说明,举例是加载单个文件的,点击进入spring官方文档。懒得点的话,我把内容也复制进这里了。


三,原理
下面我们看看springboot启动的时候,在哪执行我们的代码的。(本来以为有专门代码格式的,结果没有,文字版没法看,那就截图吧)
1,入口函数不多说

2,大概就会new一个SpringApplication的实例来run。

3,new实例时会查找所有jar包里面META-INF/spring.factories配置的org.springframework.context.ApplicationListener,并设到实例的属性listeners中。


4,再来看看run,这里就是主流程了,这方法有个局部变量listeners,不要和SpringApplication的属性listeners搞混。关注浅蓝色阴影背景部分。

4.1,先看看这个局部Listeners的来源,也是从META-INF/spring.factories下面查找,发现有
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
然后会把EventPublishingRunListener实例化,可以看到,getSpringFactoriesInstances方法里的入参args里面添加了this,就是把SpringApplication传递给EventPublishingRunListener,这样实例化的时候可以获取SpringApplication的属性listeners,最后封装成SpringApplicationRunListeners返回,属性listeners只有EventPublishingRunListener的实例。

4.2,EventPublishingRunListener实例化的时候,把之前SpringApplication的属性listeners逐一添加到initialMulticaster,当然也包括org.springframework.boot.context.config.ConfigFileApplicationListener

5,回到第4步的再来看看run,继续往下走

5.1现在看看准备环境,传递了局部的listeners,最终是调用了EventPublishingRunListener的environmentPrepared方法(参考4.1,循环里的属性listeners只有EventPublishingRunListener的实例)。


5.2EventPublishingRunListener通过initialMulticaster发布事件ApplicationEnvironmentPreparedEvent

5.3initialMulticaster发布事件,getApplicationListeners就能获取到之前SpringApplication的属性listeners(参考4.2EventPublishingRunListener实例化的时候,把之前SpringApplication的属性listeners逐一添加到initialMulticaster,当然也包括org.springframework.boot.context.config.ConfigFileApplicationListener)

5.4,最终会调用listener.onApplicationEvent(event),其中也就是会调用ConfigFileApplicationListener中的onApplicationEvent方法,当然,这里还会调用到其它Listener中的onApplicationEvent方法。

5.5,再来看看ConfigFileApplicationListener中的监听代码,事件类型是ApplicationEnvironmentPreparedEvent(参考5.2EventPublishingRunListener通过initialMulticaster发布事件ApplicationEnvironmentPreparedEvent)
看loadPostProcessors(),这里会查找META-INF/spring.factories下org.springframework.boot.env.EnvironmentPostProcessor的类,其中就包括了我们的类O2oClientEnvironmentPostProcessor
然后就会调用我们的O2oClientEnvironmentPostProcessor的postProcessEnvironment方法了,
ConfigFileApplicationListener也是EnvironmentPostProcessor类,这里有加载默认配置文件的代码,O2oClientEnvironmentPostProcessor也是参考这个类写出来的,具体信息请读者自己研究。

到这里,我们的代码就已经成功执行了。后面的就不分析了。总结一下,springboot启动的时候会初始化一系列listeners,并发布不同阶段的事件,其中ConfigFileApplicationListener会监听ApplicationEnvironmentPreparedEvent,在处理这个事件中,会查找META-INF/spring.factories下org.springframework.boot.env.EnvironmentPostProcessor的所有类,并执行其中的postProcessEnvironment方法。
网友评论