您好,登錄后才能下訂單哦!
這篇文章給大家分享的是有關SpringBoot擴展外部化配置的原理是什么的內容。小編覺得挺實用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。
在基于SpringBoot開發的應用中,我們常常會在application.properties
、application-xxx.properties
、application.yml
、application-xxx.yml
等配置文件中設置一些屬性值,然后通過@Value
、@ConfigurationProperties
等注解獲取,或者采用編碼的方式通過Environment
獲取。
# application.properties my.config.appId=demo
@RestController public class WebController { @Value("${my.config.appId}") private String appId; @Autowired private Environment env; @Autowired private ConfigurableEnvironment environment; @GetMapping("/appInfo") public String appInfo() { System.out.println(environment.getProperty("my.config.appId")); System.out.println(env.getProperty("my.config.appId")); System.out.println(appId); System.out.println(env == environment); //true return appId; } }
實際上env
和environment
是同一個對象,在Spring中ConfigurableEnvironment
是Environment
的子類,具體實現類全部是通過implements
ConfigurableEnvironment
接口來實現,所以所有可以拿到Environment
接口地方都可以強制轉換為ConfigurableEnvironment
。
ConfigurableEnvironment
繼承Environment
,Environment
繼承PropertyResolver
,主要提供了對屬性獲取方法,AbstractEnvironment
做為抽象類實現了ConfigurableEnvironment
接口方法,其內部是通過org.springframework.core.env.MutablePropertySources
來保存不同類型的屬性資源。而MutablePropertySources
內部實際上就是List<PropertySource<?>>集合。
public interface ConfigurableEnvironment extends Environment, ConfigurablePropertyResolver { void setActiveProfiles(String... profiles); void addActiveProfile(String profile); void setDefaultProfiles(String... profiles); //MutablePropertySources 內部實際上就是**List<PropertySource<?>>集合 MutablePropertySources getPropertySources(); Map<String, Object> getSystemProperties(); Map<String, Object> getSystemEnvironment(); void merge(ConfigurableEnvironment parent); }
PropertySource
是什么呢?
其實就是一個key-value集合,key就是一個配置項,value就是配置的值。
例如: 通過System.getProperties()
得到的系統屬性就是一種類型的PropertySource
,通過application.yml
配置的屬性是另一種屬性資源。當調用env.getProperty()
獲取屬性值時,會遍歷PropertySource集合,只要有一個PropertySource
中有對應屬性值則不再繼續遍歷查找,所以在集合中越靠前的屬性優先級越高。
獲取某個配置項值的訪問方式,源碼如下:org.springframework.core.env.PropertySourcesPropertyResolver#getProperty(java.lang.String, java.lang.Class<T>, boolean)
protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) { if (this.propertySources != null) { for (PropertySource<?> propertySource : this.propertySources) { if (logger.isTraceEnabled()) { logger.trace("Searching for key '" + key + "' in PropertySource '" + propertySource.getName() + "'"); } Object value = propertySource.getProperty(key); if (value != null) { if (resolveNestedPlaceholders && value instanceof String) { value = resolveNestedPlaceholders((String) value); } logKeyFound(key, propertySource, value); return convertValueIfNecessary(value, targetValueType); } } } if (logger.isTraceEnabled()) { logger.trace("Could not find key '" + key + "' in any property source"); } return null; }
實際上我們可以利用SpringBoot中的擴展點,拿到ConfigurableEnvironment
對象來獲取到MutablePropertySources
,添加自己的PropertySource
就行,例如可以訪問一個http接口,獲取外部化配置。
梯形縮進表示內部調用了下面的接口實現
1.org.springframework.boot.SpringApplicationRunListener#environmentPrepared(ConfigurableBootstrapContext, ConfigurableEnvironment)
1.ApplicationListener< org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent>
EnvironmentPostProcessorApplicationListener
1. org.springframework.boot.env.EnvironmentPostProcessor
1.org.springframework.boot.context.config.ConfigDataLoader
1.org.springframework.boot.env.PropertySourceLoader
1.org.springframework.context.ApplicationContextInitializer#initialize
1.org.springframework.boot.SpringApplicationRunListener#contextPrepared
4.org.springframework.boot.context.event.ApplicationPreparedEvent
5.org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry
org.springframework.beans.factory.config.BeanFactoryPostProcessor#postProcessBeanFactory
但是在4.BeanDefinitionRegistryPostProcessor
和5.BeanFactoryPostProcessor
中擴展時機比較晚,這個時候已經執行完包掃描,如果在這個時機添加自己的外部化配置,對于注解@ConditionalOnProperty
可能大部分不會生效。
Apollo配置中心客戶端是如何與SpringBoot整合的?
開源的Apollo配置中心默認啟動就是通過BeanFactoryPostProcessor
來擴展apollo上的配置到Spring的Environment
中,@EnableApolloConfig
注解向Spring中導入了bean com.ctrip.framework.apollo.spring.config.PropertySourcesProcessor
,PropertySourcesProcessor
同時實現了org.springframework.core.PriorityOrdered
并設置了最高的執行優先級Ordered.HIGHEST_PRECEDENCE
,但是由于包掃描已經在PropertySourcesProcessor
之前執行完成,所以即使設置了最高優先級,同樣無法解決在Spring執行包掃描階段訪問不到apllo上的配置問題。
因此在SpringBoot項目中,apollo提供了另一種啟動方式,使用配置項apollo.bootstrap.enabled = true
來解決,實現類為com.ctrip.framework.apollo.spring.boot.ApolloApplicationContextInitializer
,其主要是通過實現第2個擴展接口org.springframework.context.ApplicationContextInitializer
來提前將apollo的PropertySource
添加到Spring的Environment
中。
這樣我們就可以通過Environment
來獲取到apollo中的配置項值。而@ConditionalOnProperty
則是從Environment
獲取屬性值來判斷的條件是否成立,因此使用該接口擴展Environment,@ConditionalOnProperty
注解則可以在啟動階段正常訪問到apollo中的配置項。
感謝各位的閱讀!關于“SpringBoot擴展外部化配置的原理是什么”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,讓大家可以學到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。