前文在分析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);
}
将结果放进环境,然后重新初始化等操作。