承接前文監聽器對bootstrapContext建立的引導,筆者瞭解到其主要入口類爲BootstrapImportSelectorConfiguration。本文將基於此類進行簡單的分析java
簡單的配置類,看下源碼spring
@Configuration @Import(BootstrapImportSelector.class) public class BootstrapImportSelectorConfiguration { }
嗯,引入了延遲加載類BootstrapImportSelector,那筆者就繼續往下看下此會延遲加載哪些類,直接去觀察其主方法bootstrap
@Override public String[] selectImports(AnnotationMetadata annotationMetadata) { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); // Use names and ensure unique to protect against duplicates List<String> names = new ArrayList<>(SpringFactoriesLoader .loadFactoryNames(BootstrapConfiguration.class, classLoader)); names.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray( environment.getProperty("spring.cloud.bootstrap.sources", "")))); List<OrderedAnnotatedElement> elements = new ArrayList<>(); for (String name : names) { try { elements.add(new OrderedAnnotatedElement(metadataReaderFactory, name)); } catch (IOException e) { continue; } } AnnotationAwareOrderComparator.sort(elements); String[] classNames = elements.stream() .map(e -> e.name) .toArray(String[]::new); return classNames; }
上述的代碼很簡單,其會去加載classpath路徑下全部spring.factories文件中以org.springframework.cloud.bootstrap.BootstrapConfiguration做爲Key的全部類;
同時springcloud也支持經過設置spring.cloud.bootstrap.sources屬性來加載指定類api
筆者就先以springcloud context板塊內的spring.factories做爲分析的源頭緩存
# 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
在分析上述的源碼以前,筆者必須得清楚如今bootstrapContext加載的配置文件默認爲bootstrap.properties抑或是bootstrap.yml,其屬性已經被加入至相應的Environment對象中。基於此咱們再往下走,以避免犯糊塗安全
配置源的加載,此Configuration主要用於肯定是否外部加載的配置屬性複寫Spring內含的環境變量。注意其是ApplicationContextInitializer接口的實現類,前文已經提到,bootstrapContext上的此接口的bean類都會被註冊至子級的SpringApplication對象上。
直接看下主要的代碼片斷把app
@Override public void initialize(ConfigurableApplicationContext applicationContext) { CompositePropertySource composite = new CompositePropertySource( BOOTSTRAP_PROPERTY_SOURCE_NAME); AnnotationAwareOrderComparator.sort(this.propertySourceLocators); boolean empty = true; // 此處爲子級的環境變量對象 ConfigurableEnvironment environment = applicationContext.getEnvironment(); // 經過PropertySourceLocator接口去加載外部配置 for (PropertySourceLocator locator : this.propertySourceLocators) { PropertySource<?> source = null; source = locator.locate(environment); if (source == null) { continue; } logger.info("Located property source: " + source); composite.addPropertySource(source); empty = false; } if (!empty) { MutablePropertySources propertySources = environment.getPropertySources(); String logConfig = environment.resolvePlaceholders("${logging.config:}"); LogFile logFile = LogFile.get(environment); if (propertySources.contains(BOOTSTRAP_PROPERTY_SOURCE_NAME)) { propertySources.remove(BOOTSTRAP_PROPERTY_SOURCE_NAME); } // 肯定屬性讀取的前後順序 insertPropertySources(propertySources, composite); // reinitialize log reinitializeLoggingSystem(environment, logConfig, logFile); setLogLevels(applicationContext, environment); // active profiles process handleIncludedProfiles(environment); } }
上述的代碼就涉及兩點,一個是經過PropertySourceLocator接口加載外部配置;一個是用於解析以spring.cloud.config爲開頭的PropertySourceBootstrapProperties屬性,默認狀況下,外部配置比內部變量有更高的優先級。具體的用戶可自行分析ide
備註:PropertiesSourceBootstrapProperties中的屬性變量可經過PropertySourceLocator接口配置this
和spring常見的解析文件同樣的操做,具體就不分析了spa
@Configuration @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) public class PropertyPlaceholderAutoConfiguration { // 配置文件屬性讀取經常使用類 @Bean @ConditionalOnMissingBean(search = SearchStrategy.CURRENT) public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() { return new PropertySourcesPlaceholderConfigurer(); } }
經過命名便會發現其跟刷新屬性的功能有關,先優先看下其類結構
@Configuration @ConditionalOnBean(ConfigurationPropertiesBindingPostProcessor.class) public class ConfigurationPropertiesRebinderAutoConfiguration implements ApplicationContextAware, SmartInitializingSingleton { }
上述代碼表示其依據於當前類環境存在ConfigurationPropertiesBindingPostProcessorBean對象纔會被應用,仔細查閱了下,發現只要有使用到@EnableConfigurationProperties註解即就會被註冊。看來此配置跟ConfigurationProperties註解也有必定的關聯性。
本文就羅列筆者比較關注的幾個地方
1.ConfigurationPropertiesRebinder對象的建立
@Bean @ConditionalOnMissingBean(search = SearchStrategy.CURRENT) public ConfigurationPropertiesRebinder configurationPropertiesRebinder( ConfigurationPropertiesBeans beans) { ConfigurationPropertiesRebinder rebinder = new ConfigurationPropertiesRebinder( beans); return rebinder; }
此類讀者能夠自行翻閱代碼,能夠發現其暴露了JMX接口以及監聽了springcloud context自定義的EnvironmentChangeEvent事件。看來其主要用來刷新ApplicationContext上的beans(含@ConfigurationProperties註解)對象集合
2.ConfigurationPropertiesBeans對象的建立
@Bean @ConditionalOnMissingBean(search = SearchStrategy.CURRENT) public ConfigurationPropertiesBeans configurationPropertiesBeans() { // ConfigurationBeanFactoryMetadata metaData = this.context.getBean( ConfigurationBeanFactoryMetadata.BEAN_NAME, ConfigurationBeanFactoryMetadata.class); ConfigurationPropertiesBeans beans = new ConfigurationPropertiesBeans(); beans.setBeanMetaDataStore(metaData); return beans; }
配合ConfigurationProperties註解的表演,其會緩存ApplicationContext上的全部含有ConfigurationProperties註解的bean。與第一點所提的ConfigurationPropertiesRebinder對象搭配使用
3.實例化結束後刷新父級ApplicationContext上的屬性
@Override public void afterSingletonsInstantiated() { // refresh parent application context if (this.context.getParent() != null) { // TODO: make this optional? (E.g. when creating child contexts that prefer to // be isolated.) ConfigurationPropertiesRebinder rebinder = context .getBean(ConfigurationPropertiesRebinder.class); for (String name : context.getParent().getBeanDefinitionNames()) { rebinder.rebind(name); } } }
與屬性讀取的加解密有關,跟JDK的keystore也有必定的關聯,具體就不去解析了。讀者可自行分析
此處本文插入這個Bean的加載問題,由於筆者發現SpringApplication會被調用兩次,那麼ApplicationContext實例也會被建立兩次。那麼基於@Configuration修飾過的自定義的Bean是否是也會被加載兩次呢??
通過在cloud環境下編寫了一個簡單的Bean
package com.example.clouddemo; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.annotation.Configuration; /** * @author nanco * ------------- * ------------- * @create 19/8/20 */ @Configuration public class TestApplication implements ApplicationContextAware { @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { System.out.println("application parent context id: " + applicationContext.getParent().getId()); System.out.println("application context id: " + applicationContext.getId()); } }
且在application.properties文件中指定spring.application.name=springChild以及bootstrap.properties文件中也指定spring.application.name=springBootstrap
運行main方法以後打印的關鍵信息以下
application parent context id: bootstrap application context id: springBootstrap-1
通過在org.springframework.boot.context.ContextIdApplicationContextInitializer類上進行斷點調試發現只有用戶級ApplicationContext被建立的過程當中會實例化用戶自定義Bean。也就是說bootstrapContext並不會去實例化用戶自定義的Bean,這樣就很安全。
那麼爲什麼如此呢?其實很簡單,由於bootstrapContext指定的source類只有BootstrapImportSelectorConfiguration,並無用戶編寫的啓動類,也就沒法影響用戶級別Context的Bean加載實例化了~~~而且該類上無@EnableAutoConfiguration註解,代表其也不會去處理spring.factories文件中@EnableAutoConfiguration註解key對應的配置集合。
1.針對springcloud context模塊下的以BootstrapConfiguration做爲Key的自動配置類,除了PropertySourceBootstrapConfiguration自動類的應用範圍在子級ApplicationContext,其它三個均有做用於父級ApplicationContext。
2.關於外部源文件的屬性,默認狀況下其有更高的優先級於本地系統以及環境變量。固然用戶也能夠經過修改spring.cloud.config.allowOverride/spring.cloud.config.overrideSystemProperties/spring.cloud.config.overrideNone屬性來進行優先級更改,經過此,用戶須要複寫PropertySourceLocator接口來進行配置
package com.example.cloud.external.resource; import org.springframework.cloud.bootstrap.config.PropertySourceLocator; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.PropertySource; import java.util.HashMap; import java.util.Map; /** * @author nanco * ------------- * cloud-demo * ------------- * @create 2019/1/15 19:40 * @descrption **/ @Configuration public class ExternalPropertySourceLocator implements PropertySourceLocator { private static final String EXTERNAL_KEY = "external"; @Override public PropertySource<?> locate(Environment environment) { Map<String, Object> externalMap = new HashMap<>(); // user custom property externalMap.put("username", "nanco"); externalMap.put("password", "nanco123"); externalMap.put("mail", "nancoasky@gmail.com"); // application property externalMap.put("spring.cloud.config.allowOverride", true); externalMap.put("spring.cloud.config.overrideSystemProperties", false); //system擁有更高的優先級 externalMap.put("spring.cloud.config.overrideNone", false); return new MapPropertySource(EXTERNAL_KEY, externalMap); } }
最後將上述的類放置在META-INF/spring.factories文件中即可以生效了
# Bootstrap components org.springframework.cloud.bootstrap.BootstrapConfiguration=\ com.example.cloud.external.resource.ExternalPropertySourceLocator
3.關於針對@ConfigurationProperties
註解的Beans對象的刷新操做,本文只講解了JMX方式去調用,若是與第三方插件結合,應該會有更多的形式。
4.針對監聽器環節的分析到本章暫時告一段落,下一篇便分析cloud-context板塊spring.factories文件中以org.springframework.boot.autoconfigure.EnableAutoConfiguration做爲Key的類集合。 熟悉的氣息再次降臨,火燒眉毛ing....