欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

解決SpringBoot加載application.properties配置文件的坑

 更新時(shí)間:2021年08月18日 14:50:15   作者:流云一號(hào)  
這篇文章主要介紹了SpringBoot加載application.properties配置文件的坑,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

SpringBoot加載application.properties配置文件的坑

事情的起因是這樣的

一次,本人在現(xiàn)場(chǎng)升級(jí)程序,升級(jí)完成后進(jìn)行測(cè)試,結(jié)果接口調(diào)用都報(bào)了這么個(gè)錯(cuò)誤:

大概意思是https接口需要證書校驗(yàn),這就奇怪了,項(xiàng)目啟動(dòng)加載的是包外的application.properties配置文件,配置文件里沒(méi)有配置使用https啊。本人馬上檢查了下包內(nèi)的application.properties配置文件,發(fā)現(xiàn)包內(nèi)確實(shí)配置了https相關(guān)的配置項(xiàng):

明明包外的配置文件優(yōu)先級(jí)高于包內(nèi)的,為啥包內(nèi)的一部分配置項(xiàng)起作用了呢,我們了解的配置文件優(yōu)先級(jí)是這樣的:

這是為啥呢?后來(lái)才了解到除了高優(yōu)先級(jí)覆蓋低優(yōu)先級(jí)外,還有一條重要的規(guī)則:如有不同內(nèi)容,高優(yōu)先級(jí)和低優(yōu)先級(jí)形成互補(bǔ)配置。這下才恍然大悟,我包外的配置文件里把https相關(guān)的配置項(xiàng)注釋掉了,相當(dāng)于沒(méi)有這個(gè)配置項(xiàng),但是包內(nèi)的配置文件有,根據(jù)互補(bǔ)原則,包內(nèi)的這幾個(gè)配置項(xiàng)起作用了。

問(wèn)題原因找到了,如何解決呢?

要不我把包內(nèi)的那幾個(gè)配置項(xiàng)也注釋掉,重新打個(gè)包?其實(shí)不必這么麻煩,通過(guò)-Dspring.config.location命令直接指定包外的配置文件就可以了,試了下,果然沒(méi)有問(wèn)題了。問(wèn)題雖然解決了,但是還有些疑問(wèn),為啥指定包外的配置文件后就不存在互補(bǔ)情況了呢?

通過(guò)閱讀springboot相關(guān)源碼,找到了答案:

大概意思是:

如果-Dspring.config.location指定了配置文件,則只加載指定的那一個(gè)配置文件,如果沒(méi)有專門指定配置文件則遍歷包外、包內(nèi)相關(guān)的配置文件,按照高優(yōu)先級(jí)覆蓋低優(yōu)先級(jí)和互補(bǔ)原則進(jìn)行加載。

弄明白這些問(wèn)題后,實(shí)地部署項(xiàng)目的時(shí)候,保險(xiǎn)起見還是通過(guò)-Dspring.config.location命令直接指定加載的配置文件比較好,避免出現(xiàn)一些不必要的麻煩。

Spring Boot加載application.properties探究

基于Spring Boot的多Module項(xiàng)目中,有許多公共的配置項(xiàng),為避免在每個(gè)接入層都配置一遍,一個(gè)設(shè)想是在公共依賴的Module的application.properties(application.yml)中進(jìn)行配置。原來(lái)的配置文件位于接入層的classpath,可由Spring Boot打包插件打入,一旦置于公共Module,配置文件就不再直接被打入jar包,而是位于內(nèi)嵌的jar包中,并不確認(rèn)Spring Boot會(huì)去掃內(nèi)嵌于jar包中的application文件,因此可行性有待驗(yàn)證。

探索

實(shí)驗(yàn)準(zhǔn)備,項(xiàng)目結(jié)構(gòu)如下所示:

Demo
 - web(接入層)
  - src
   - main
    - java
    - resources
     - application.properties // 1 
   - test
  - pom.xml
 - common(公共層)
  - src
   - main
    - java
    - resources
     - application-dev.properties // 2
 - pom.xml(父Module pom)

接入層為web,在resources下存在application.properties,內(nèi)容為spring.profiles.active=dev,目的是為了激活dev的profile

公共同為common,在在resources下存在application-dev.properties,內(nèi)容為name=demo_test

因此,如果配置項(xiàng)name=demo_test能夠被應(yīng)用成功讀取到,那么就驗(yàn)證了在背景中提及的設(shè)想

實(shí)驗(yàn)結(jié)果:成功讀取

原理分析

一般地,Spring Boot 默認(rèn)的配置文件名稱為:application.properties或application.yml,為方便描述,統(tǒng)一為application.properties。從Spring Boot 官方文檔得知,Spring Boot可以從下述位置按順序加載配置文件

A /config subdirectory of the current directory(file:./config/)
The current directory(file:./)
A classpath /config package(classpath:/config/)
The classpath root(classpath:/)

優(yōu)先級(jí)表述如下:

The list is ordered by precedence (properties defined in locations higher in the list override those defined in lower locations).

也即是說(shuō),排在前邊的優(yōu)先級(jí)高于排在后邊的。這里有幾層隱含的含義,在官方文檔中并沒(méi)有表述清楚,為方便記憶與理解

總結(jié)如下:

1、上邊的4個(gè)位置均可放置配置文件(application.properties)

它們之間是一個(gè)并集關(guān)系而不是互斥關(guān)系,Spring Boot 默認(rèn)都會(huì)加載到它們,而不是加載到高優(yōu)先級(jí)的配置文件之后就停止加載低優(yōu)先級(jí)的

2、如果在兩個(gè)以上的application.properties里配置

同一個(gè)配置項(xiàng)(如: name=demo),那么優(yōu)先級(jí)高的配置項(xiàng)會(huì)生效

舉個(gè)例子,項(xiàng)目結(jié)構(gòu)如下

src
 - main
  - resources
   - config
    - application.properties // 3 (k1=v1, k2=v2)
   - application.properties // 4  (k1=v3, k4=v4)

在優(yōu)先級(jí)排名第3的配置文件中,存在兩個(gè)配置項(xiàng)(k1=v1, k2=v2);在優(yōu)先級(jí)排名第4的配置文件中,存在兩個(gè)配置項(xiàng)(k1=v3, k4=v4)。內(nèi)存中,四個(gè)配置項(xiàng)都存在,但生效的配置項(xiàng)只有三個(gè):k1=v1,k2=v2,k4=v4,而k1=v3由于優(yōu)先級(jí)比較低,并不生效

在Spring Boot應(yīng)用啟動(dòng)過(guò)程中,需要?jiǎng)?chuàng)建ConfigurableEnvironment,當(dāng)Environment創(chuàng)建完,Spring 會(huì)發(fā)布ApplicationEnvironmentPreparedEvent事件,告知Environment創(chuàng)建完畢。ConfigFileApplicationListener會(huì)監(jiān)聽這個(gè)事件,在事件處理中,使用Spring SPI機(jī)制加載EnvironmentPostProcessor集合,并回調(diào)EnvironmentPostProcessor#postProcessEnvironment方法。很巧的是,ConfigFileApplicationListener同時(shí)也實(shí)現(xiàn)了EnvironmentPostProcessor,因此,會(huì)回調(diào)到自身的postProcessEnvironment方法中。

注:下邊的源碼基于Spring Boot 2.1.10.RELEASE

// org.springframework.boot.SpringApplication#run(java.lang.String...)
public ConfigurableApplicationContext run(String... args) {
 // ...(省略)
 listeners.starting();
 try {
  ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
  // 創(chuàng)建ConfigurableEnvironment
  ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
  // ...(省略)
}
// org.springframework.boot.SpringApplication#run(java.lang.String...)
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
  ApplicationArguments applicationArguments) {
 // Create and configure the environment
 ConfigurableEnvironment environment = getOrCreateEnvironment();
 configureEnvironment(environment, applicationArguments.getSourceArgs());
 ConfigurationPropertySources.attach(environment);
 // 發(fā)布ApplicationEnvironmentPreparedEvent事件
 listeners.environmentPrepared(environment);
 // ...(省略)
}

// org.springframework.boot.SpringApplicationRunListeners#environmentPrepared
public void environmentPrepared(ConfigurableEnvironment environment) {
 for (SpringApplicationRunListener listener : this.listeners) {
  listener.environmentPrepared(environment);
 }
}

// org.springframework.boot.context.event.EventPublishingRunListener#environmentPrepared
public void environmentPrepared(ConfigurableEnvironment environment) {
    // 發(fā)布ApplicationEnvironmentPreparedEvent事件
 this.initialMulticaster
   .multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));
}
// org.springframework.boot.context.config.ConfigFileApplicationListener
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
 // 利用Spring SPI機(jī)制加載EnvironmentPostProcessor
 List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
 postProcessors.add(this);
 AnnotationAwareOrderComparator.sort(postProcessors);
 for (EnvironmentPostProcessor postProcessor : postProcessors) {
  // 回調(diào)
  postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());
 }
}

在postProcessEnvironment回調(diào)中,添加了RandomValuePropertySource,并調(diào)用內(nèi)部類Loader的load方法,對(duì)application.properties進(jìn)行加載

// org.springframework.boot.context.config.ConfigFileApplicationListener
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
 addPropertySources(environment, application.getResourceLoader());
}

protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
    // 添加`RandomValuePropertySource`到Environment
 RandomValuePropertySource.addToEnvironment(environment);
 // load()方法是重點(diǎn);
 new Loader(environment, resourceLoader).load();
}
// org.springframework.boot.context.config.ConfigFileApplicationListener.Loader#load()

public void load() {
 this.profiles = new LinkedList<>();
 this.processedProfiles = new LinkedList<>();
 this.activatedProfiles = false;
 this.loaded = new LinkedHashMap<>();
 // 以上四個(gè)變量默認(rèn)狀態(tài)為空集合或false,用于在下邊迭代的過(guò)程中收集數(shù)據(jù)
 // 初始化profiles集合,如果存在active的profile,會(huì)將activatedProfiles變量設(shè)置為true
 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();
}

初始化profiles集合,如果存在active的profile,會(huì)將activatedProfiles變量設(shè)置為true。這里需要注意的是,在案例demo中,是將spring.profiles.active=dev寫在classpath的application.properties,而此時(shí)application.properties都還沒(méi)有讀取,所以該配置項(xiàng)并未生效。故此,active的profile指的是那些通過(guò)system property、system enviroment、手動(dòng)調(diào)用AbstractEnvironment#setActiveProfiles等方式設(shè)置active profile,他們的共同特點(diǎn)是優(yōu)先級(jí)都較高,配置項(xiàng)初始化早,在執(zhí)行l(wèi)oad方法前就已生效

先往profiles集合添加null,表示將要加載那些跟profile無(wú)關(guān)的application.properties,并且如果沒(méi)有active profile,那還會(huì)加載名為default的profile

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()) {
      // 加載名為`default`的profile
   Profile defaultProfile = new Profile(defaultProfileName, true);
   this.profiles.add(defaultProfile);
  }
 }
}

initializeProfiles方法執(zhí)行完畢之后,只要profiles非空,就從隊(duì)首取出并進(jìn)行加載。profiles是個(gè)雙端隊(duì)列,加載的過(guò)程有可能往隊(duì)列里添加或者移除元素,因此使用的是while (!this.profiles.isEmpty())的判斷方式。

接著看load(profile, this::getPositiveProfileFilter, addToLoaded(MutablePropertySources::addLast, false));

該方法結(jié)構(gòu)很清晰,迭代每一個(gè)location(搜索路徑),如果搜索路徑是個(gè)目錄(以/結(jié)尾),則獲取配置文件名,然后結(jié)合搜索路徑+配件文件名對(duì)配置文件進(jìn)行加載。這兒隱含一層意思:location可以直接指定為配置文件,但是此種方式不被推薦使用,因?yàn)檫@會(huì)導(dǎo)致Profile機(jī)制失效,建議還是按正常的姿勢(shì)去使用

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));
 });
}

獲取搜索路徑,

可由spring.config.location指定或者spring.config.additional-location + classpath:/,classpath:/config/,file:./,file:./config/。注意此處,spring.config.location指定的搜索順序跟定義的順序相反,例如指定的位置為a, b, c,則按c, b, a的順序進(jìn)行搜索,而搜索順序反應(yīng)的是配置項(xiàng)的優(yōu)先級(jí),在上邊已提過(guò),不再贅述

private Set<String> getSearchLocations() {
 // 若通過(guò) spring.config.location 指定配置文件目錄,則到指定路徑查找,不再走默認(rèn)的搜索路徑和額外添加的路徑,可以指定多個(gè),以逗號(hào)進(jìn)行分隔
 // CONFIG_LOCATION_PROPERTY = spring.config.location
 if (this.environment.containsProperty(CONFIG_LOCATION_PROPERTY)) {
  return getSearchLocations(CONFIG_LOCATION_PROPERTY);
 }
 
 // 除了默認(rèn)路徑,還可以通過(guò) spring.config.additional-location 指定額外的搜索路徑
 // CONFIG_ADDITIONAL_LOCATION_PROPERTY = spring.config.additional-location
 Set<String> locations = getSearchLocations(CONFIG_ADDITIONAL_LOCATION_PROPERTY);
 
 // 默認(rèn)搜索路徑
 // DEFAULT_SEARCH_LOCATIONS = classpath:/,classpath:/config/,file:./,file:./config/
 locations.addAll(
   asResolvedSet(ConfigFileApplicationListener.this.searchLocations, DEFAULT_SEARCH_LOCATIONS));
 return locations;
}

該方法將搜索路徑或者指定的配置文件名以逗號(hào)分割后倒置

private Set<String> asResolvedSet(String value, String fallback) {
 List<String> list = Arrays.asList(StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(
   (value != null) ? this.environment.resolvePlaceholders(value) : fallback)));
 Collections.reverse(list);
 return new LinkedHashSet<>(list);
}

獲取待搜索的配置文件名,可由spring.config.name指定或者使用默認(rèn)值application,同上面的搜索路徑一樣,spring.config.name指定的搜索順序跟定義的順序相反

private Set<String> getSearchNames() {
 // 若通過(guò) spring.config.name 指定配置文件名稱,則只會(huì)搜索該名稱的配置文件,可以指定多個(gè),以逗號(hào)進(jìn)行分隔
 // CONFIG_NAME_PROPERTY = spring.config.name
 if (this.environment.containsProperty(CONFIG_NAME_PROPERTY)) {
  String property = this.environment.getProperty(CONFIG_NAME_PROPERTY);
  return asResolvedSet(property, null);
 }

 // 默認(rèn)搜索的配置文件名稱為application
 // DEFAULT_NAMES = application
 return asResolvedSet(ConfigFileApplicationListener.this.names, DEFAULT_NAMES);
}

在我們的案例中,沒(méi)有通過(guò)spring.config.location指定配置文件目錄,也沒(méi)有通過(guò)spring.config.name指定配置文件名,因此都采用默認(rèn)值,且順序倒置:

localtion:file:./config/, file:./, classpath:/config/, classpath:/

config.name: application

且只在classpath:/放有配置文件application.properties與application-dev.properties

接著,遍歷propertySourceLoaders對(duì)配置文件進(jìn)行加載。propertySourceLoaders是在構(gòu)造Loader類時(shí)進(jìn)行初始化的,它利用Spring SPI機(jī)制對(duì)實(shí)現(xiàn)類進(jìn)行加載,默認(rèn)實(shí)現(xiàn)類有兩個(gè)

PropertiesPropertySourceLoader: 加載.properties與.xml的配置文件

YamlPropertySourceLoader: 加載.yml和.yaml的配置文件

private void load(String location, String name, Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
 // ...(省略)
 Set<String> processed = new HashSet<>();
 for (PropertySourceLoader loader : this.propertySourceLoaders) {
  for (String fileExtension : loader.getFileExtensions()) {
      // .properties\.xml\.yml\.yaml
   if (processed.add(fileExtension)) {
    loadForFileExtension(loader, location + name, "." + fileExtension, profile, filterFactory,
      consumer);
   }
  }
 }
}
private void loadForFileExtension(PropertySourceLoader loader, String prefix, String fileExtension, Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
 DocumentFilter defaultFilter = filterFactory.getDocumentFilter(null);
 DocumentFilter profileFilter = filterFactory.getDocumentFilter(profile);
 if (profile != null) {
  // Try profile-specific file & profile section in profile file (gh-340)
  // profileSpecificFile = file:./application-dev.properties
  String profileSpecificFile = prefix + "-" + profile + fileExtension;
  // 加載profile對(duì)應(yīng)的配置文件
  load(loader, profileSpecificFile, profile, defaultFilter, consumer);
  load(loader, profileSpecificFile, profile, profileFilter, consumer);
  // Try profile specific sections in files we've already processed
  for (Profile processedProfile : this.processedProfiles) {
   if (processedProfile != null) {
    String previouslyLoaded = prefix + "-" + processedProfile + fileExtension;
    load(loader, previouslyLoaded, profile, profileFilter, consumer);
   }
  }
 }
 // Also try the profile-specific section (if any) of the normal file
 // 加載非profile的配置文件
 load(loader, prefix + fileExtension, profile, profileFilter, consumer);
}

使用resourceLoader到location獲取配置文件資源,resourceLoader也是在Loader類構(gòu)造的時(shí)候初始化的,默認(rèn)是DefaultResourceLoader,它是Spring提供的ResourceLoader的默認(rèn)實(shí)現(xiàn)類,能夠獲取classpath資源以及URL資源或類URL資源,資源用Resource進(jìn)行抽象表示。

此處,已經(jīng)可以解釋文章探索實(shí)驗(yàn)的結(jié)果:資源的獲取是靠Spring提供的DefaultResourceLoader實(shí)現(xiàn)的,它能夠?qū)崿F(xiàn)classpath的掃描,進(jìn)而加載資源,因此,只要是classpath下的配置文件,無(wú)論是否在內(nèi)嵌jar包內(nèi),最終都能加載到

有了Loader,以及Resource,就可以進(jìn)行資源的加載,加載的結(jié)果是List,代表對(duì)配置文件屬性源的抽象以及封裝。用DocumentFilter對(duì)滿足條件的Document進(jìn)行過(guò)濾,滿足條件的則被添加進(jìn)MutablePropertySources中

private void load(PropertySourceLoader loader, String location, Profile profile, DocumentFilter filter, DocumentConsumer consumer) {
 try {
  Resource resource = this.resourceLoader.getResource(location);
  // ...(省略)
  String name = "applicationConfig: [" + location + "]";
  List<Document> documents = loadDocuments(loader, name, resource);
  // ...(省略)
  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));
   // ...(省略)
}

最終,被加載的配置文件存在loaded變量中,調(diào)用addLoadedPropertySources方法,將loaded倒置之后添加進(jìn)environment的PropertySources中,倒置的目的,是為了使profile的配置文件優(yōu)先級(jí)更高。而一旦將配置項(xiàng)添加進(jìn)environment的屬性源集合中,應(yīng)用程序就能正確取讀到配置項(xiàng)。

// org.springframework.boot.context.config.ConfigFileApplicationListener.Loader#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();
   }
  }
 }
}

其實(shí),application-{profile}.properties配置文件加載位置同標(biāo)準(zhǔn)的application.properties,但是它有一點(diǎn)顯著不同的是,無(wú)論application-{profile}.properties放哪,profile類的配置文件優(yōu)先級(jí)最高,當(dāng)配置項(xiàng)沖突時(shí),總是"覆蓋"一切非profile的配置文件

總結(jié)

本文開篇提出一個(gè)問(wèn)題:在依賴的公共Module的classpath放置application.properties,Spring Boot應(yīng)用能否正確讀取?之后通過(guò)案例進(jìn)行實(shí)驗(yàn),證明了此行為的可行性。為了了解Spring Boot對(duì)application.properties加載的過(guò)程,先是閱讀了Spring Boot 官方文檔對(duì)application.properties的介紹,并對(duì)其中關(guān)于配置項(xiàng)優(yōu)先級(jí)的模糊描述做了進(jìn)一步的解釋。接著從源碼的角度,對(duì)application.properties的加載過(guò)程從頭到尾簡(jiǎn)單介紹了一遍,了解到ResourceLoader及其默認(rèn)實(shí)現(xiàn)類DefaultResourceLoader正是用于從classpath加載資源,因此能成功加載內(nèi)嵌jar包中位于classpath的application.properties

最后,介紹了Spring Boot對(duì)于PropertySource優(yōu)先級(jí)處理的原則:后贏策略(last-wins),加載的過(guò)程按代碼定義的順序先加載,放入數(shù)據(jù)源之前進(jìn)行倒置(reverse)放入,在后邊的反而優(yōu)先級(jí)高

題外話

1、配置文件前2優(yōu)先級(jí)位置分別是:file:./config/、file:./,在IDEA中是指當(dāng)前項(xiàng)目的/config目錄以及當(dāng)前項(xiàng)目根目錄。如果是多module項(xiàng)目,那么當(dāng)前項(xiàng)目指的是父module目錄。其實(shí)在IDEA環(huán)境中使用這倆位置的配置文件意義不大,更多的,是與發(fā)布系統(tǒng)結(jié)合,發(fā)布系統(tǒng)將服務(wù)打成Executable Jar之后,將應(yīng)用相關(guān)的基礎(chǔ)配置信息(如server.port、Apollo apollo.meta\env )配置在./config/或者./,用以覆蓋項(xiàng)目?jī)?nèi)有可能誤配或漏配的選項(xiàng)

2、本文的一些規(guī)律,不單適用于application.properties,還適用于別的配置文件。例如:配置項(xiàng)優(yōu)先級(jí)原則,基本思想是:由Spring加載所有的屬性源到Environment中,通過(guò)屬性源的方式將配置項(xiàng)進(jìn)行隔離,不同的屬性源互不干擾,在此基礎(chǔ)上,靠前的屬性源的配置項(xiàng)優(yōu)先級(jí)高。這種行為是Spring默認(rèn)的行為,該行為定義在PropertySourcesPropertyResolver,也意味著,我們可以自定義PropertyResolver,來(lái)改變這種默認(rèn)的行為,實(shí)現(xiàn)自定義的優(yōu)先級(jí)順序,達(dá)到我們的目的

3、關(guān)于application.properties的加載過(guò)程,還有很多細(xì)節(jié)未曾提及,這并非意味著不重要,而是一篇文章難以面面俱到,而陷入源碼細(xì)節(jié)容易一葉障目。從問(wèn)題出發(fā),梳理主干脈絡(luò),把握核心思想,是為首要條件,之后每次根據(jù)需要,像剝洋蔥般一層層深入,能更容易掌握知識(shí)

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • mybatis中批量更新多個(gè)字段的2種實(shí)現(xiàn)方法

    mybatis中批量更新多個(gè)字段的2種實(shí)現(xiàn)方法

    當(dāng)我們使用mybatis的時(shí)候,可能經(jīng)常會(huì)碰到一批數(shù)據(jù)的批量更新問(wèn)題,因?yàn)槿绻粭l數(shù)據(jù)一更新,那每一條數(shù)據(jù)就需要涉及到一次數(shù)據(jù)庫(kù)的操作,本文主要介紹了mybatis中批量更新多個(gè)字段的2種實(shí)現(xiàn)方法,感興趣的可以了解一下
    2023-09-09
  • Guava事件總線應(yīng)用場(chǎng)景最佳實(shí)踐

    Guava事件總線應(yīng)用場(chǎng)景最佳實(shí)踐

    這篇文章主要為大家介紹了Guava事件總線應(yīng)用場(chǎng)景最佳實(shí)踐,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-12-12
  • 2020最新版idea激活教程(推薦)

    2020最新版idea激活教程(推薦)

    這篇文章主要介紹了2020最新版idea激活教程,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-02-02
  • java 文件和byte互轉(zhuǎn)的實(shí)例

    java 文件和byte互轉(zhuǎn)的實(shí)例

    下面小編就為大家分享一篇java 文件和byte互轉(zhuǎn)的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2017-11-11
  • SpringBoot中application.properties、application.yaml、application.yml區(qū)別

    SpringBoot中application.properties、application.yaml、applicati

    本文主要介紹了SpringBoot中application.properties、application.yaml、application.yml區(qū)別,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2024-04-04
  • springboot動(dòng)態(tài)調(diào)用實(shí)現(xiàn)類方式

    springboot動(dòng)態(tài)調(diào)用實(shí)現(xiàn)類方式

    這篇文章主要介紹了springboot動(dòng)態(tài)調(diào)用實(shí)現(xiàn)類方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • Java數(shù)組(Array)最全匯總(上篇)

    Java數(shù)組(Array)最全匯總(上篇)

    這篇文章主要介紹了Java數(shù)組(Array)最全匯總(上篇),本文章內(nèi)容詳細(xì),通過(guò)案例可以更好的理解數(shù)組的相關(guān)知識(shí),本模塊分為了三部分,本次為上篇,需要的朋友可以參考下
    2023-01-01
  • java中的類URL與URLConnection使用介紹

    java中的類URL與URLConnection使用介紹

    這篇文章主要為大家介紹了java中的類URL與URLConnection使用介紹,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-06-06
  • 詳解Java設(shè)計(jì)模式編程中的策略模式

    詳解Java設(shè)計(jì)模式編程中的策略模式

    這篇文章主要介紹了詳解Java設(shè)計(jì)模式編程中的策略模式,策略模式強(qiáng)調(diào)對(duì)對(duì)象的封裝使用,比如文中舉的錦囊妙計(jì)的例子便很生動(dòng),需要的朋友可以參考下
    2016-02-02
  • SSM項(xiàng)目中使用攔截器和過(guò)濾器的實(shí)現(xiàn)示例

    SSM項(xiàng)目中使用攔截器和過(guò)濾器的實(shí)現(xiàn)示例

    這篇文章主要介紹了SSM項(xiàng)目中使用攔截器和過(guò)濾器的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-04-04

最新評(píng)論