springboot application.properties load profile procedure

Recently I discovered, with questions to read the source code would be a more eclectic approach. So you can quickly grasp some of the core issues and logic, they would not get into specific details but a waste of time. If you want to know some of the specific points of time and then turn it out to read, so that when you finished solving a problem is also gradually from the outer to the inner layer, specifically, to understand it from the rough.

Then enter the formal content paper

We often come into contact application.properties when using springboot for development (application.yml too, here for simplicity) profile, we can focus on a few major problems

submit questions

1) then springboot startup, application.properties When was loaded?

2) The default loading process is kind of how?

3) configure multiple environmental profile of the specified time is how loaded?

Question 1: application.properties when the file is loaded?

First of all, we need to know springboot using event listeners application.properties way to load the configuration file. In other words, the program starts at the beginning of setting up a EventListener, when a later things done publish a corresponding Event trigger out the listener.

After springboot Environment object is created, it will go to trigger a listener EventPublishingRunListener, will publish a ApplicationEnvironmentPreparedEvent, this Event will trigger ConfigFileApplicationListener. ConfigFileApplicationListener this listener class that is loaded application.properties core classes.

Let's start with the run method, with the code

getRunListeners (args) method will get SpringApplicationRunListener from spring.factories interface implementation class, as

这个时候EventPublishingRunListener这个监听器将会被加载,然后调用prepareEnvironment的时候顺带着被做实参传入,我们进入prepareEnvironment方法。

我们看到,prepareEnvironment方法会创建一个Environment实例对象并进行一些配置处理,而后就会触发EventPublishingRunListener监听器,执行其environmentPrepared方法。这里我们可以猜测到EventPublishingRunListener调度角色,会构造出对应的事件并分发给对应的监听器。

进入EventPublishingRunListener,我们看到该方法创建了一个ApplicationEnvironmentPreparedEvent事件,然后广播了出去。

 

那么这个新建的事件广播给哪些监听器呢?getApplicationListeners方法将会给出答案,我们断点就可以看到

我们看到了一开始说的ConfigFileApplicationListener监听器出现了,invokeListener方法将会触发该监听器的onApplicationEvent方法。我们再跟进ConfigFileApplicationListener的onApplicationEvent方法

继续跟进onApplicationEnvironmentPreparedEvent方法

我们看到loadPostProcessors方法去加载了Environment的后置处理器,并且调用postProcessEnvironment方法。ConfigFileApplicationListener也实现了EnvironmentPostProcessor接口,所以postProcessEnvironment方法再当前类中有实现。我们跟进当前类的postProcessEnvironment方法。

再跟进addPropertySources方法

addPropertySources方法将会实例化一个加载器Loader并开始加载配置。

到这里我们基本上知道第一个问题的答案,Springboot是在Environment创建以后发布一个事件触发ConfigFileApplicationListener监听器并开始加载application.properties配置的。

问题2:默认加载过程是怎么样的?

 上面,我们知道addPropertySources方法最终是实例化一个Loader并调用其load方法去加载配置文件,显然application.properties这个配置文件的加载过程也就再这里实现。

我们先看看Loader的构造方法做了什么

这个构造方法值得关注的一点是它从spring.factories中加载了PropertySourceLoader接口的实现类,该接口顾名思义就是用来加载配置文件资源的,接口的实现类如下

 

前者加载properties、xml扩展文件,后者加载yml、yaml扩展文件

回到addPropertySources方法

在Loader实例化完毕以后,就调用其load()方法,我们跟进load方法看看

很显然,load方法实现了加载文件的核心逻辑。我们先关注一下initiallizeProfiles()方法

我们注意到,一开始添加了一个null,这个是为了保证默认的application.properties会被优先处理。

而initializeProfiles方法希望从Environment里面拿到现有的profiles配置,不过显然默认是不会有的,所以size==1,将会拿到Environment中默认的profiles,而默认的profiles只会有一个default。

所以initializeProfiles方法最终将产生一个这样的数据

profiles = [null, "default"]

回到load方法,继续往下看

首先profile=null会被找到,由于为null,所以将会直接调用另一个load方法,我们跟进另一个load方法

这里我们看到getSearchLocations将会获取所有待搜索的目录位置,我们进入getSearchLocations方法

可以看到,Environment默认是没有配置的,所以最终会找到默认的常量值,如下:

到这里,我们通过getSearchLocations方法获取了默认会被搜索的文件夹路径,回到刚才的load方法

 

找到待搜索目录以后,将会遍历该目录,再通过getSearchNames()方法获取待搜索文件的名字,我们看看getSearchNames()方法

显然和getSearchLocations方法一样,最后会取到默认值,如

我们看到,被搜索的配置文件名默认是"application"。

到这里,我们拿到了待搜索的目录、待搜索的文件名,下面就是去目录下搜索对应的文件了,回到刚刚的load方法

跟进另一个load方法

我们已经获得了name=application,所以直接进入下面一段代码。

首先遍历了propertySourceLoaders,这个ProperySourceLoader也就是我们一开始Loader实例化的时候从spring.factories文件加载出来的两个加载器properties、yml文件加载器。

在遍历中,将会根据:目录 + 文件名 + 文件扩展名 去加载默认的配置文件,例如:classpath:/application.properties,我们跟进loadForFileExtension方法

我们一开始的profile=null,所以这里将直接调用另一个load方法,跟进看看,这个load方法代码较长,截取核心如下

首先loadDocuments方法将会使用load加载器加载配置文件为Document,然后document会被遍历分别处理。这里的consumer是从一开始的load方法传入的,我们回头看看第一个load,如

我们看到consumer是addToLoaded方法返回出来的,并且将MutablePropertySourcs的addLast作为方法应用传递进去,我们进入addToLoaded方法

我们看到,之前Loader加载出来的每个document的PropertySource将会被添加到loaded对象当中,这里我们稍微关注一下loaded对象是什么结构,如

loaded对象承担的责任是存储Profile和Properties的属性值的映射,所以我们第一个application.properties文件加载到loaded以后就变成 key=null,value=Application配置文件的属性值了。

这样,我们一个application.properties文件也就被加载出来了,还需要最后一步将它添加到Environment环境变量当中,我们回到最开始的load方法,如

addLoadedPropertySources方法将会把每个consumer添加到loaded对象当中的PropertySource给添加到Environment中,我们跟进addLoadedPropertySources方法

这里做了一个遍历,我们跟进addLoadedPropertySource方法

source被添加到了destination中,也就是Environment的成员对象MutablePropertySources当中。

到这里,第二个问题的解答就结束了,默认的加载过程也就是将application.properties或者application.yml通过加载器加载为PropertySource,并添加到Environment当中。

问题3:配置多环境指定profile的时候又是什么原理?

配置多环境切换的时候我们通常是这样做的

application.properties文件中指定那个启用,这里配置启用dev文件

那么,为什么application.properties指定了spring.profiles.active以后就会自动加载对应的配置文件呢?比如这里指定了dev,那么就会去加载application-dev.properties配置文件。

首先,我们是在application.properties文件中指定的,所以加载什么配置文件也肯定是在application.properties文件加载完以后决定的,我们进入刚才讨论第二个问题的时候loadDocuments的位置,如图

我们看到application.properties配置的profile会被拿出来,再进入addActiveProfiles看看添加到哪里去

直接被添加到了原先profiles=[null, "default"]的集合当中,并且最后还把"default"给移除掉了。这说明当加载完application.properties以后,相应的profiles将被重新指定,我们回到一开始的load方法中

load方法将找到配置的"dev",并且跟之前一样调用load方法。我们再一路跟进,直到loadForFileExtension方法

我们看到,当有profile存在的时候,文件名 = 前缀 + profile + 扩展名,例如 application-dev.properties,并加载该文件,获取PropertySource添加到loaded中,并最终加载到Environment当中。到这里,第三个问题就解答完毕了。

springboot加载配置文件还有很多小细节值得了解,比如中文乱码问题、加载顺序问题、重复配置覆盖问题等等~

 

Guess you like

Origin www.cnblogs.com/lay2017/p/11456556.html
Recommended