Nacos原始碼分析六、NacosConfigBootstrapConfiguration配置類
技術標籤:NacosSpringCloudAlibabajavaspring cloud alibabanacos
前文在分析BootStrapApplicationListener時得到當引入nacos時,會載入NacosConfigBootstrapConfiguration配置類:
@Configuration(proxyBeanMethods = false) @ConditionalOnProperty(name = "spring.cloud.nacos.config.enabled", matchIfMissing = true) public class NacosConfigBootstrapConfiguration { @Bean @ConditionalOnMissingBean public NacosConfigProperties nacosConfigProperties() { return new NacosConfigProperties(); } @Bean @ConditionalOnMissingBean public NacosConfigManager nacosConfigManager( NacosConfigProperties nacosConfigProperties) { return new NacosConfigManager(nacosConfigProperties); } @Bean public NacosPropertySourceLocator nacosPropertySourceLocator( NacosConfigManager nacosConfigManager) { return new NacosPropertySourceLocator(nacosConfigManager); } }
NacosConfigProperties這個是nacos的bootstrap屬性類。
我們先看NacosConfigManager這個類:
public class NacosConfigManager { private static final Logger log = LoggerFactory.getLogger(NacosConfigManager.class); private static ConfigService service = null; private NacosConfigProperties nacosConfigProperties; public NacosConfigManager(NacosConfigProperties nacosConfigProperties) { this.nacosConfigProperties = nacosConfigProperties; // Compatible with older code in NacosConfigProperties,It will be deleted in the // future. createConfigService(nacosConfigProperties); } /** * Compatible with old design,It will be perfected in the future. */ static ConfigService createConfigService( NacosConfigProperties nacosConfigProperties) { if (Objects.isNull(service)) { synchronized (NacosConfigManager.class) { try { if (Objects.isNull(service)) { service = NacosFactory.createConfigService( nacosConfigProperties.assembleConfigServiceProperties()); } } catch (NacosException e) { log.error(e.getMessage()); throw new NacosConnectionFailureException( nacosConfigProperties.getServerAddr(), e.getMessage(), e); } } } return service; } public ConfigService getConfigService() { if (Objects.isNull(service)) { createConfigService(this.nacosConfigProperties); } return service; } public NacosConfigProperties getNacosConfigProperties() { return nacosConfigProperties; } }
可以看到,主要是為了獲得ConfigService例項,這個和我們一開始使用的測試類程式碼基本一致。
nacosConfigProperties.assembleConfigServiceProperties()主要是一些初始化配置:
public Properties assembleConfigServiceProperties() { Properties properties = new Properties(); properties.put(SERVER_ADDR, Objects.toString(this.serverAddr, "")); properties.put(USERNAME, Objects.toString(this.username, "")); properties.put(PASSWORD, Objects.toString(this.password, "")); properties.put(ENCODE, Objects.toString(this.encode, "")); properties.put(NAMESPACE, Objects.toString(this.namespace, "")); properties.put(ACCESS_KEY, Objects.toString(this.accessKey, "")); properties.put(SECRET_KEY, Objects.toString(this.secretKey, "")); properties.put(CLUSTER_NAME, Objects.toString(this.clusterName, "")); properties.put(MAX_RETRY, Objects.toString(this.maxRetry, "")); properties.put(CONFIG_LONG_POLL_TIMEOUT, Objects.toString(this.configLongPollTimeout, "")); properties.put(CONFIG_RETRY_TIME, Objects.toString(this.configRetryTime, "")); properties.put(ENABLE_REMOTE_SYNC_CONFIG, Objects.toString(this.enableRemoteSyncConfig, "")); String endpoint = Objects.toString(this.endpoint, ""); if (endpoint.contains(":")) { int index = endpoint.indexOf(":"); properties.put(ENDPOINT, endpoint.substring(0, index)); properties.put(ENDPOINT_PORT, endpoint.substring(index + 1)); } else { properties.put(ENDPOINT, endpoint); } enrichNacosConfigProperties(properties); return properties; }
然後是NacosPropertySourceLocator類,這個是nacos的屬性源定位器。我們先看一下什麼地方載入它的。
先回來看spring-cloud-context的spring.factories檔案定義:
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
關注PropertySourceBootstrapConfiguration這個配置類,他是用來做遠端配置處理的,這是個初始化器,在準備好上下文,重新整理前applyInitializers方法執行。
他有個注入屬性,我們已經注入了nacos
的NacosPropertySourceLocator
了:
看一下initialize方法:
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
List<PropertySource<?>> composite = new ArrayList<>();
AnnotationAwareOrderComparator.sort(this.propertySourceLocators);
boolean empty = true;
ConfigurableEnvironment environment = applicationContext.getEnvironment();
for (PropertySourceLocator locator : this.propertySourceLocators) {
Collection<PropertySource<?>> source = locator.locateCollection(environment);
if (source == null || source.size() == 0) {
continue;
}
List<PropertySource<?>> sourceList = new ArrayList<>();
for (PropertySource<?> p : source) {
sourceList.add(new BootstrapPropertySource<>(p));
}
logger.info("Located property source: " + sourceList);
composite.addAll(sourceList);
empty = false;
}
if (!empty) {
MutablePropertySources propertySources = environment.getPropertySources();
String logConfig = environment.resolvePlaceholders("${logging.config:}");
LogFile logFile = LogFile.get(environment);
for (PropertySource<?> p : environment.getPropertySources()) {
if (p.getName().startsWith(BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
propertySources.remove(p.getName());
}
}
insertPropertySources(propertySources, composite);
reinitializeLoggingSystem(environment, logConfig, logFile);
setLogLevels(applicationContext, environment);
handleIncludedProfiles(environment);
}
}
先將屬性源定位器propertySourceLocators
排序,然後遍歷進行定位,封裝成PropertySource
並放進集合裡。
locateCollection方法:
default Collection<PropertySource<?>> locateCollection(Environment environment) {
return locateCollection(this, environment);
}
static Collection<PropertySource<?>> locateCollection(PropertySourceLocator locator,
Environment environment) {
PropertySource<?> propertySource = locator.locate(environment);
if (propertySource == null) {
return Collections.emptyList();
}
if (CompositePropertySource.class.isInstance(propertySource)) {
Collection<PropertySource<?>> sources = ((CompositePropertySource) propertySource)
.getPropertySources();
List<PropertySource<?>> filteredSources = new ArrayList<>();
for (PropertySource<?> p : sources) {
if (p != null) {
filteredSources.add(p);
}
}
return filteredSources;
}
else {
return Arrays.asList(propertySource);
}
}
locator.locate(environment)就進入了我們的NacosPropertySourceLocator實現中了。
@Override
public PropertySource<?> locate(Environment env) {
//設定環境
nacosConfigProperties.setEnvironment(env);
//獲取配置服務
ConfigService configService = nacosConfigManager.getConfigService();
if (null == configService) {
log.warn("no instance of config service found, can't load config from nacos");
return null;
}
//超時30秒
long timeout = nacosConfigProperties.getTimeout();
//屬性源建造器
nacosPropertySourceBuilder = new NacosPropertySourceBuilder(configService,
timeout);
//dataid的名字
String name = nacosConfigProperties.getName();
//字首
String dataIdPrefix = nacosConfigProperties.getPrefix();
if (StringUtils.isEmpty(dataIdPrefix)) {
dataIdPrefix = name;
}
//字首為空的話預設就是spring.application.name
if (StringUtils.isEmpty(dataIdPrefix)) {
dataIdPrefix = env.getProperty("spring.application.name");
}
//建立複合屬性源
CompositePropertySource composite = new CompositePropertySource(
NACOS_PROPERTY_SOURCE_NAME);
loadSharedConfiguration(composite);
loadExtConfiguration(composite);
loadApplicationConfiguration(composite, dataIdPrefix, nacosConfigProperties, env);
return composite;
}
主要看一下loadApplicationConfiguration方法:
private void loadApplicationConfiguration(
CompositePropertySource compositePropertySource, String dataIdPrefix,
NacosConfigProperties properties, Environment environment) {
//副檔名
String fileExtension = properties.getFileExtension();
//分組,預設DEFAULT_GROUP
String nacosGroup = properties.getGroup();
// load directly once by default 直接預設配置檔案載入一次
loadNacosDataIfPresent(compositePropertySource, dataIdPrefix, nacosGroup,
fileExtension, true);
// load with suffix, which have a higher priority than the default
//檔名加字尾來一次
loadNacosDataIfPresent(compositePropertySource,
dataIdPrefix + DOT + fileExtension, nacosGroup, fileExtension, true);
// Loaded with profile, which have a higher priority than the suffix
//有環境配置的更高級別
for (String profile : environment.getActiveProfiles()) {
String dataId = dataIdPrefix + SEP1 + profile + DOT + fileExtension;
loadNacosDataIfPresent(compositePropertySource, dataId, nacosGroup,
fileExtension, true);
}
}
然後是loadNacosDataIfPresent方法:
private void loadNacosDataIfPresent(final CompositePropertySource composite,
final String dataId, final String group, String fileExtension,
boolean isRefreshable) {
if (null == dataId || dataId.trim().length() < 1) {
return;
}
if (null == group || group.trim().length() < 1) {
return;
}
NacosPropertySource propertySource = this.loadNacosPropertySource(dataId, group,
fileExtension, isRefreshable);
this.addFirstPropertySource(composite, propertySource, false);
}
然後是loadNacosPropertySource方法:
private NacosPropertySource loadNacosPropertySource(final String dataId,
final String group, String fileExtension, boolean isRefreshable) {
//重新整理過了
if (NacosContextRefresher.getRefreshCount() != 0) {
if (!isRefreshable) {
//不重新整理,直接快取取
return NacosPropertySourceRepository.getNacosPropertySource(dataId,
group);
}
}
return nacosPropertySourceBuilder.build(dataId, group, fileExtension,
isRefreshable);
}
第一次沒有快取,呼叫nacosPropertySourceBuilder.build方法:
NacosPropertySource build(String dataId, String group, String fileExtension,
boolean isRefreshable) {
Map<String, Object> p = loadNacosData(dataId, group, fileExtension);
NacosPropertySource nacosPropertySource = new NacosPropertySource(group, dataId,
p, new Date(), isRefreshable);
NacosPropertySourceRepository.collectNacosPropertySource(nacosPropertySource);
return nacosPropertySource;
}
先載入資料,然後結果封裝成NacosPropertySource
,放進快取。
loadNacosData方法:
private Map<String, Object> loadNacosData(String dataId, String group,
String fileExtension) {
String data = null;
try {
data = configService.getConfig(dataId, group, timeout);
if (StringUtils.isEmpty(data)) {
log.warn(
"Ignore the empty nacos configuration and get it based on dataId[{}] & group[{}]",
dataId, group);
return EMPTY_MAP;
}
if (log.isDebugEnabled()) {
log.debug(String.format(
"Loading nacos data, dataId: '%s', group: '%s', data: %s", dataId,
group, data));
}
Map<String, Object> dataMap = NacosDataParserHandler.getInstance()
.parseNacosData(data, fileExtension);
return dataMap == null ? EMPTY_MAP : dataMap;
}
catch (NacosException e) {
log.error("get data from Nacos error,dataId:{}, ", dataId, e);
}
catch (Exception e) {
log.error("parse data from Nacos error,dataId:{},data:{},", dataId, data, e);
}
return EMPTY_MAP;
}
就是configService.getConfig的呼叫。
然後回來PropertySourceBootstrapConfiguration的initialize方法後半部分:
if (!empty) {
MutablePropertySources propertySources = environment.getPropertySources();
String logConfig = environment.resolvePlaceholders("${logging.config:}");
LogFile logFile = LogFile.get(environment);
for (PropertySource<?> p : environment.getPropertySources()) {
if (p.getName().startsWith(BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
//刪除bootstrapProperties,因為bootstrap已經處理完了
propertySources.remove(p.getName());
}
}
//將結果放入環境的MutablePropertySources中
insertPropertySources(propertySources, composite);
//重新初始化log系統
reinitializeLoggingSystem(environment, logConfig, logFile);
//設定log級別
setLogLevels(applicationContext, environment);
//處理包含的環境配置
handleIncludedProfiles(environment);
}
將結果放進環境,然後重新初始化等操作。