Spring?Boot常用功能Profile詳解
入口
相關(guān)邏輯的入口是listener類:ConfigFileApplicationListener,當(dāng)容器廣播器觸發(fā)ApplicationEnvironmentPreparedEvent事件時,ConfigFileApplicationListener會收到廣播器的通知,進而執(zhí)行onApplicationEnvironmentPreparedEvent方法
入口處代碼:
@Override public void onApplicationEvent(ApplicationEvent event) { if (event instanceof ApplicationEnvironmentPreparedEvent) { onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event); } if (event instanceof ApplicationPreparedEvent) { onApplicationPreparedEvent(event); } }
接下來onApplicationEnvironmentPreparedEvent方法會加載容器中的EnvironmentPostProcessor并進行遍歷,調(diào)用他們的postProcessEnvironment方法,我們可以看一下此時的PostProcessor有哪些:
我們發(fā)現(xiàn)ConfigFileApplicationListener本身也是其中一個PostProcessor :)
我們直接進入ConfigFileApplicationListener的postProcessEnvironment方法,它會調(diào)用一個addPropertySources方法
protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) { RandomValuePropertySource.addToEnvironment(environment); new Loader(environment, resourceLoader).load(); }
隨機屬性值部分暫且不表,我們看下loader.load方法
public void load() { this.profiles = new LinkedList<>(); this.processedProfiles = new LinkedList<>(); this.activatedProfiles = false; this.loaded = new LinkedHashMap<>(); initializeProfiles(); while (!this.profiles.isEmpty()) { Profile profile = this.profiles.poll(); if (profile != null && !profile.isDefaultProfile()) { addProfileToEnvironment(profile.getName()); } load(profile, this::getPositiveProfileFilter, addToLoaded(MutablePropertySources::addLast, false)); this.processedProfiles.add(profile); } resetEnvironmentProfiles(this.processedProfiles); load(null, this::getNegativeProfileFilter, addToLoaded(MutablePropertySources::addFirst, true)); addLoadedPropertySources(); }
load方法的重點邏輯-initializeProfiles()
private void initializeProfiles() { // The default profile for these purposes is represented as null. We add it // first so that it is processed first and has lowest priority. this.profiles.add(null); Set<Profile> activatedViaProperty = getProfilesActivatedViaProperty(); this.profiles.addAll(getOtherActiveProfiles(activatedViaProperty)); // Any pre-existing active profiles set via property sources (e.g. // System properties) take precedence over those added in config files. addActiveProfiles(activatedViaProperty); if (this.profiles.size() == 1) { // only has null profile for (String defaultProfileName : this.environment.getDefaultProfiles()) { Profile defaultProfile = new Profile(defaultProfileName, true); this.profiles.add(defaultProfile); } } }
add(null) 可以處理application.properties/yml,接下來getProfilesActivatedViaProperty方法會從spring.profiles.active和spring.profiles.include配置中讀取激活的profile~(這兩者也是相對常用的配置);再接下來會判斷profiles大小是否為1,是的話會添加一個default profile,如圖所示:
這也解釋了為什么spring.profiles.default必須定義在其他屬性源(命令行啟動參數(shù)),因為這時候分散文件屬性元還沒有被解析到!
回到load方法,如果profiles不為空,就會進行while遍歷,對其調(diào)用另外一個load方法:
(load-number2方法):
private void load(Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) { getSearchLocations().forEach((location) -> { boolean isFolder = location.endsWith("/"); Set<String> names = isFolder ? getSearchNames() : NO_SEARCH_NAMES; names.forEach((name) -> load(location, name, profile, filterFactory, consumer)); }); }
我們可以看到還會調(diào)用到另一個load方法
(load-number3方法):
private void load(String location, String name, Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) { if (!StringUtils.hasText(name)) { for (PropertySourceLoader loader : this.propertySourceLoaders) { if (canLoadFileExtension(loader, location)) { load(loader, location, profile, filterFactory.getDocumentFilter(profile), consumer); return; } } } Set<String> processed = new HashSet<>(); for (PropertySourceLoader loader : this.propertySourceLoaders) { for (String fileExtension : loader.getFileExtensions()) { if (processed.add(fileExtension)) { loadForFileExtension(loader, location + name, "." + fileExtension, profile, filterFactory, consumer); } } } }
這里的propertySourceLoaders包括:
是不是很熟悉?這就是properties和yaml分別對應(yīng)的PropertySourceLoader~
下面代碼會調(diào)用loadForFileExtension方法,而這個方法又會調(diào)用新的load方法(??):
(load-number4方法):
private void load(PropertySourceLoader loader, String location, Profile profile, DocumentFilter filter, DocumentConsumer consumer) { try { Resource resource = this.resourceLoader.getResource(location); if (resource == null || !resource.exists()) { if (this.logger.isTraceEnabled()) { StringBuilder description = getDescription("Skipped missing config ", location, resource, profile); this.logger.trace(description); } return; } if (!StringUtils.hasText(StringUtils.getFilenameExtension(resource.getFilename()))) { if (this.logger.isTraceEnabled()) { StringBuilder description = getDescription("Skipped empty config extension ", location, resource, profile); this.logger.trace(description); } return; } String name = "applicationConfig: [" + location + "]"; List<Document> documents = loadDocuments(loader, name, resource); if (CollectionUtils.isEmpty(documents)) { if (this.logger.isTraceEnabled()) { StringBuilder description = getDescription("Skipped unloaded config ", location, resource, profile); this.logger.trace(description); } return; } List<Document> loaded = new ArrayList<>(); for (Document document : documents) { if (filter.match(document)) { addActiveProfiles(document.getActiveProfiles()); addIncludedProfiles(document.getIncludeProfiles()); loaded.add(document); } } Collections.reverse(loaded); if (!loaded.isEmpty()) { loaded.forEach((document) -> consumer.accept(profile, document)); if (this.logger.isDebugEnabled()) { StringBuilder description = getDescription("Loaded config file ", location, resource, profile); this.logger.debug(description); } } } catch (Exception ex) { throw new IllegalStateException("Failed to load property " + "source from location '" + location + "'", ex); } }
這個方法會讀取application-profile.properties/yaml文件,并加載文件內(nèi)激活的profile(如果有的話)
到這里已經(jīng)讀取到了所有的配置信息,主邏輯大概結(jié)束了,還差最后一個步驟:addLoadedPropertySources:
private void addLoadedPropertySources() { MutablePropertySources destination = this.environment.getPropertySources(); List<MutablePropertySources> loaded = new ArrayList<>(this.loaded.values()); Collections.reverse(loaded); String lastAdded = null; Set<String> added = new HashSet<>(); for (MutablePropertySources sources : loaded) { for (PropertySource<?> source : sources) { if (added.add(source.getName())) { addLoadedPropertySource(destination, lastAdded, source); lastAdded = source.getName(); } } } }
這里會將所有屬性加入到“destination”,即environment.getPropertySources(),到這里,加載配置屬性的邏輯就完成了。
到此這篇關(guān)于Spring Boot常用功能Profile詳解的文章就介紹到這了,更多相關(guān)Spring Boot Profile內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Springcloud GateWay網(wǎng)關(guān)配置過程圖解
這篇文章主要介紹了Springcloud GateWay網(wǎng)關(guān)配置過程圖解,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-12-12基于紅黑樹插入操作原理及java實現(xiàn)方法(分享)
下面小編就為大家分享一篇基于紅黑樹插入操作原理及java實現(xiàn)方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2017-12-12SpringBoot使用WebSocket實現(xiàn)前后端交互的操作方法
springboot使用websocket有兩種方式,一種是實現(xiàn)簡單的websocket,另外一種是實現(xiàn)STOMP協(xié)議,本篇講述如何使用springboot實現(xiàn)簡單的websocket,需要的朋友可以參考下2022-04-04Spring5中SpringWebContext方法過時的解決方案
這篇文章主要介紹了Spring5中SpringWebContext方法過時的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-01-01SpringBoot配置Redis實現(xiàn)保存獲取和刪除數(shù)據(jù)
本文主要介紹了SpringBoot配置Redis實現(xiàn)保存獲取和刪除數(shù)據(jù),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,感興趣的小伙伴們可以參考一下2021-06-06Java數(shù)據(jù)結(jié)構(gòu)中圖的進階詳解
在Java學(xué)習(xí)與應(yīng)用中,數(shù)據(jù)結(jié)構(gòu)無疑是每個人都要接觸的難點,為了更好的學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)這一塊內(nèi)容,用圖來理解便是最好的方式,讓我們一起來了解本篇內(nèi)容圖的進階2022-01-01