? ? ? SpringCloud為我們提供了bootstrap.properties的屬性文件,我們可以在該屬性文件里做我們的服務配置。可是,我們知道SpringBoot已經為我們提供了做服務配置的屬性文件application.properties,那么這兩個配置文件有什么區別呢?在SpringCloud里是否能用bootstrap代替application做服務的配置?要解決這個問題,我們必須先討論一下SpringCloud的引導。
?一、? 官方文檔描述
引導應用程序上下文
一個Spring Cloud應用程序通過創建一個“引導”上下文來進行操作,這個上下文是主應用程序的父上下文。開箱即用,負責從外部源加載配置屬性,還解密本地外部配置文件中的屬性。這兩個上下文共享一個Environment
,這是任何Spring應用程序的外部屬性的來源。Bootstrap屬性的優先級高,因此默認情況下不能被本地配置覆蓋。
引導上下文使用與主應用程序上下文不同的外部配置約定,因此使用bootstrap.yml
?application.yml
(或.properties
)代替引導和主上下文的外部配置。例:bootstrap.yml
spring:application:name: foocloud:config:uri: ${SPRING_CONFIG_URI:http://localhost:8888}
如果您的應用程序需要服務器上的特定于應用程序的配置,那么設置spring.application.name
(在bootstrap.yml
或application.yml
)中是個好主意。
您可以通過設置spring.cloud.bootstrap.enabled=false
(例如在系統屬性中)來完全禁用引導過程。
?
二、引導上下文
?1.?關于引導上下文位置
??
? ? ? 這里我們可以發現幾個關鍵的類,其中BootstrapApplicationListener是核心中的核心,可自行查看源碼
? ? ?這個類是一個監聽器,它用于監聽ApplicationEnvironmentPreparedEvent事件,而EventPublishingRunListener在SpringBoot啟動時會觸發該事件。如果不理解的這個類的朋友請務必先了解SpringBoot啟動過程
? ? 2.這個上下文是主應用程序的父上下文
? ? ??這個工作主要分為兩個層面:1.創建上下文引導 2.設置為其為當前程序的父級上下文
? ? ?
?? 1) 我們先看看onApplicationEvent方法,該方法首先讀取spring.cloud.bootstrap.enabled的屬性值如果為false,那么就直接return。這也就是官方文檔里的說明可以用此屬性禁用引導的理由。
? ?2)緊接著它會從當前應用程序SpringApplication試著在所有的ApplicationInitializer中獲取ParentContextApplicationContextInitializer,如果找到的話就把該類下的parent做為引導上下文。
? ?3)如果沒有找到ParentContextApplicationContextInitializer,則通過?bootstrapServiceContext方法來創建引導上下文,其中如下代碼請大家留意下:
List<String> names = SpringFactoriesLoader.loadFactoryNames(BootstrapConfiguration.class, classLoader);
? ? ? 看到SpringFactoriesLoader不用想一定會在META-INF/spring.factoies里找配置的BootstrapConfiguration的進行實例化
? ? ? ? ? ? ?
?4)通過如下代碼創建引導上下文對象:
SpringApplicationBuilder builder = new SpringApplicationBuilder().profiles(environment.getActiveProfiles()).bannerMode(Mode.OFF).environment(bootstrapEnvironment).properties("spring.application.name:" + configName).registerShutdownHook(false).logStartupInfo(false).web(false);if (environment.getPropertySources().contains("refreshArgs")) {// If we are doing a context refresh, really we only want to refresh the// Environment, and there are some toxic listeners (like the// LoggingApplicationListener) that affect global static state, so we need a// way to switch those off.builder.application().setListeners(filterListeners(builder.application().getListeners()));}List<Class<?>> sources = new ArrayList<>();for (String name : names) {Class<?> cls = ClassUtils.resolveClassName(name, null);try {cls.getDeclaredAnnotations();}catch (Exception e) {continue;}sources.add(cls);}AnnotationAwareOrderComparator.sort(sources);builder.sources(sources.toArray(new Class[sources.size()]));final ConfigurableApplicationContext context = builder.run();
5)最后通過如下方法設置引導上下文為當前應用程序的上下文:
// Make the bootstrap context a parent of the app contextaddAncestorInitializer(application, context);
?
? ?3. 負責從外部源加載配置屬性,還解密本地外部配置文件中的屬性
??
? ? 開箱即用,理解起來很簡單。通過2.2分析,引導程序在SpringBoot的啟動前就幫我們創建好了,當然也就開箱即用了。
下面我們看一下spring-cloud-context.jar下的META-INF/spring.factoies文件:
? ??


# AutoConfiguration org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration,\ org.springframework.cloud.autoconfigure.RefreshAutoConfiguration,\ org.springframework.cloud.autoconfigure.RefreshEndpointAutoConfiguration,\ org.springframework.cloud.autoconfigure.LifecycleMvcEndpointAutoConfiguration# Application Listeners org.springframework.context.ApplicationListener=\ org.springframework.cloud.bootstrap.BootstrapApplicationListener,\ org.springframework.cloud.bootstrap.LoggingSystemShutdownListener,\ org.springframework.cloud.context.restart.RestartListener# Bootstrap components org.springframework.cloud.bootstrap.BootstrapConfiguration=\ org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration,\ org.springframework.cloud.bootstrap.encrypt.EncryptionBootstrapConfiguration,\ org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration,\ org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration
?我們來看一下 ?BootstrapConfiguration下面配置的引導程序類:
?org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration:這個類主要解析加載外部化配置屬性
?org.springframework.cloud.bootstrap.encrypt.EncryptionBootstrapConfiguration:主要配置文件中前綴為{cipher}的相關解密,熟悉spring-boot-starter-security在springcloud應用的朋友一定不陌生
?org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration:主要是監聽EnvironmentChangeEvent事件用于刷新@ConfigurationProperties標記的配置
?org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration:主要解析配置文件中的${}占位符
?
?4. 這兩個上下文共享一個Environment
? ? ? ?既然引導上下文為當前主程序的父級上下文,那么就可以確定他們共享Environment,至于為什么請閱讀文章第一部分
?5. BootStrap屬性的優先級高,因此默認情況下不能被本地配置覆蓋
? ? ??對于引導程序bootstrap.yml比application.yml優先級更高,更不可能被application.yml文件里的所覆蓋
?三、總結
1)引導程序上下文在prepareEnvironment的階段就會被創建,創建時會讀取bootstrap.properties|yml 在內容作為引導配置, 因此bootstrap優先于application加載。引導程序非常類似于bios,而bootstrap.application就相當于設置bios的相關參數
2)boostrap的屬性文件在以下情景下會使用:
配置中心:config-server里請用bootstrap屬性文件
? 解密屬性文件時,最好使用bootstrap屬性文件
需要自定義引導程序時使用bootstrap屬性文件,主要一定不要被我們主程序掃描到
3)application會覆蓋bootstrap中的非引導配置,因此不建議兩種類型配置文件同時存在。簡單粗暴的做法是在springcloud應用中用bootstrap屬性文件代替application一統江湖嘛,畢竟Envrionment是共享的。
4) ?在閱讀官方文檔時,一定要結合源代碼深入分析,才能更好的理解其用意