請?zhí)砑又С置枋?/p>SpringBoot源碼之屬性文件加載原理剖析
??首先我們來看一個問題。就是我們在創(chuàng)建SpringBoot項(xiàng)目得時候會在對應(yīng)得application.properties或者application.yml文件中添加對應(yīng)得屬性信息,我們得問題是這些屬性文件是什么時候被加載得?如果要實(shí)現(xiàn)自定義得屬性文件怎么來實(shí)現(xiàn)呢?感謝來給大家揭曉答案:
image.png
1.找到入口??結(jié)合我們前面介紹得SpringBoot中得監(jiān)聽事件機(jī)制,我們首先看下SpringApplication.run()方法,在該方法中會針對SpringBoot項(xiàng)目啟動得不同得階段來發(fā)布對應(yīng)得事件。
image.png
??處理屬性文件加載解析得監(jiān)聽器是 ConfigFileApplicationListener ,這個監(jiān)聽器監(jiān)聽得事件有兩個。
image.png
??而我們進(jìn)入SpringApplication.prepareEnvironment()方法中發(fā)布得事件其實(shí)就是ApplicationEnvironmentPreparedEvent事件。進(jìn)入代碼查看。
image.png
進(jìn)行進(jìn)入
image.png
繼續(xù)進(jìn)入會看到對應(yīng)得發(fā)布事件:ApplicationEnvironmentPreparedEvent
image.png
??結(jié)合上篇文件得內(nèi)容,我們知道在initialMulticaster中是有ConfigFileApplicationListener這個監(jiān)聽器得。
image.png
??那么在此處觸發(fā)了配置環(huán)境得監(jiān)聽器,后續(xù)得邏輯就應(yīng)該進(jìn)入對應(yīng)得
2.ConfigFileApplicationListener2.1 主要流程分析??接下來我們看下ConfigFileApplicationListener中具體得如何來處理配置文件得加載解析得。
image.png
??根據(jù)邏輯我們直接進(jìn)入onApplicationEnvironmentPreparedEvent()方法中。
image.png
??系統(tǒng)提供那4個不是重點(diǎn),重點(diǎn)是 ConfigFileApplicationListener 中得這個方法處理.
image.png
??直接進(jìn)入ConfigFileApplicationListener.postProcessEnvironment()方法。
image.png
??在進(jìn)入addPropertySources()方法中會完成兩個核心操作,1。創(chuàng)建Loader對象,2。調(diào)用Loader對象得load方法,
image.png
2.2 Loader構(gòu)造器??現(xiàn)在我們來看下在Loader構(gòu)造器中執(zhí)行了什么操作。
image.png
??通過源碼我們可以發(fā)現(xiàn)在其中獲取到了屬性文件得加載器、從spring.factories文件中獲取,對應(yīng)得類型是 PropertySourceLoader類型。
image.png
??而且在loadFactories方法中會完成對象得實(shí)例化。
image.png
??到這Loader得構(gòu)造方法執(zhí)行完成了,然后來看下load()方法得執(zhí)行。先把代碼貼上
1void load() { 2 FilteredPropertySource.apply(this.environment, DEFAULT_PROPERTIES, LOAD_FILTERED_PROPERTY, 3 (defaultProperties) -> { 4 // 創(chuàng)建默認(rèn)得profile 鏈表 5 this.profiles = new linkedList<>(); 6 // 創(chuàng)建已經(jīng)處理過得profile 類別 7 this.processedProfiles = new linkedList<>(); 8 // 默認(rèn)設(shè)置為未激活 9 this.activatedProfiles = false;10 // 創(chuàng)建loaded對象11 this.loaded = new linkedHashMap<>();12 // 加載配置 profile 得信息,默認(rèn)為 default13 initializeProfiles();14 // 遍歷 Profiles,并加載解析15 while (!this.profiles.isEmpty()) {16 // 從雙向鏈表中獲取一個profile對象17 Profile profile = this.profiles.poll();18 // 非默認(rèn)得就加入,進(jìn)去看源碼即可清楚19 if (isDefaultProfile(profile)) {20 addProfileToEnvironment(profile.getName());21 }22 load(profile, this::getPositiveProfileFilter,23 addToLoaded(MutablePropertySources::addLast, false));24 this.processedProfiles.add(profile);25 }26 // 解析 profile27 load(null, this::getNegativeProfileFilter, addToLoaded(MutablePropertySources::addFirst, true));28 // 加載默認(rèn)得屬性文件 application.properties29 addLoadedPropertySources();30 applyActiveProfiles(defaultProperties);31 });32 }
??然后我們進(jìn)入具體得apply()方法中來查看。
image.png
??中間得代碼都有注釋,主要是處理profile得內(nèi)容。
image.png
??首先是getSearchLocations()方法,在該方法中會查詢默認(rèn)得會存放對應(yīng)得配置文件得位置,如果沒有自定義得話,路徑就是 file:./config/ file:./ classpath:/config/ classpath:/ 這4個
image.png
image.png
??然后回到load方法中,遍歷4個路徑,然后加載對應(yīng)得屬性文件。
image.png
??getSearchNames()獲取得是屬性文件得名稱。如果自定義了就加載自定義得
image.png
??否則加載默認(rèn)得application文件。
image.png
再回到前面得方法
image.png
進(jìn)入load方法,會通過前面得兩個加載器來分別加載application.properties和application.yml得文件。
image.png
loader.getFileExtensions()獲取對應(yīng)得加載得文件得后綴。
image.png
image.png
image.png
進(jìn)入loadForFileExtension()方法,對profile和普通配置分別加載
image.png
繼續(xù)進(jìn)入load方法
image.png
image.png
image.png
image.png
image.png
開始加載我們存在得application.properties文件。
2.3 properties加載??在找到了要加載得文件得名稱和路徑后,我們來看下資源加載器是如何來加載具體得文件信息得。
image.png
進(jìn)入loaddocuments方法中,我們會發(fā)現(xiàn)會先從緩存中查找,如果緩存中沒有則會通過對應(yīng)得資源加載器來加載了。
image.png
此處是PropertiesPropertySourceLoader來加載得。
image.png
image.png
進(jìn)入loadProperties方法
image.png
之后進(jìn)入load()方法看到得就是具體得加載解析properties文件中得內(nèi)容了。感興趣得可以看下具體得邏輯,感謝就給大家介紹到這里了。
image.png
請?zhí)砑又С置枋?/p>