SpringBoot 的配置解析是通過 Environment 來實現的。
Environment:
與屬性相關的 Environment 對象的作用是為用戶提供一個方便的服務接口,用於配置屬性源並從中解析屬性。
Environment 本身實現了 PropertyResolver 接口,最終會委托給 PropertySourcesPropertyResolver 去解析配置。
org.springframework.core.env.PropertySourcesPropertyResolver.getProperty
1 protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) { 2 if (this.propertySources != null) { 3 for (PropertySource<?> propertySource : this.propertySources) { // 1. 循環 PropertySources 4 if (logger.isTraceEnabled()) { 5 logger.trace("Searching for key '" + key + "' in PropertySource '" + 6 propertySource.getName() + "'"); 7 } 8 Object value = propertySource.getProperty(key); // 2. 從 PropertySource 中獲取 key 對應的配置 9 if (value != null) { 10 if (resolveNestedPlaceholders && value instanceof String) { 11 value = resolveNestedPlaceholders((String) value); // 3. 解析占位符 ${} 12 } 13 logKeyFound(key, propertySource, value); 14 return convertValueIfNecessary(value, targetValueType); // 4. 轉換成指定類型 15 } 16 } 17 } 18 if (logger.isTraceEnabled()) { 19 logger.trace("Could not find key '" + key + "' in any property source"); 20 } 21 return null; 22 }


最終獲取配置就是去各種 PropertySource 中去取 key 對應的配置值,SpringBoot 支持多種類型的 PropertySource 擴展:
RandomValuePropertySource -- 對 random.int, random.long, random.uuid 的支持
MapPropertySource -- 對 K,V 形式的配置支持
JsonPropertySource -- 對 Json 類型的配置支持
SystemEnvironmentPropertySource -- 對配置 key 中使用 .、下划線、中划線、大小寫的支持
CommandLinePropertySource -- 對啟動命令參數的支持
ServletContextPropertySource -- 對 ServletContext 的 initParam 的支持
JndiPropertySource -- 對 JNDI 類型的配置支持

@PropertySource 注解的處理
ConfigurationClassParser#doProcessConfigurationClass()
在處理 @PropertySource 時,會加載注解中定義的資源文件,然后添加到 Environment#propertySources 中。
添加時,如果指定了多個資源文件,則后面定義的會放到 propertySources 的前面。這個會影響配置優先級,即后定義的優先級高
// 對所有的 @PropertySource 而言的 // 單個 @PropertySource 中定義多個資源文件地址也受這個規則影響 if (this.propertySourceNames.isEmpty()) { propertySources.addLast(propertySource); } else { String firstProcessed = this.propertySourceNames.get(this.propertySourceNames.size() - 1); propertySources.addBefore(firstProcessed, propertySource); }
注意:
這個規則是對所有的 @PropertySource 注解生效的,因為 ConfigurationClassParser 只有一個實例,所以 propertySourceNames 變量也是共用一個,所以它會對所有的 @PropertiesSource 注解生效。
從表現上來看就是:
1. 從全局來看,后加載的 @PropertySource 資源文件優先級會排在前面
2. 從局部來看,@PropertySource 中指定多個資源文件時,后定義的資源文件優先級更高
