nacos config包主要引入了以下几个依赖(初始化组件)
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
com.alibaba.cloud.nacos.NacosConfigBootstrapConfiguration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.alibaba.cloud.nacos.NacosConfigAutoConfiguration,\
com.alibaba.cloud.nacos.endpoint.NacosConfigEndpointAutoConfiguration
org.springframework.boot.diagnostics.FailureAnalyzer=\
com.alibaba.cloud.nacos.diagnostics.analyzer.NacosConnectionFailureAnalyzer
- NacosConfigBootstrapConfiguration
public class NacosConfigBootstrapConfiguration {
@Bean
@ConditionalOnMissingBean
//初始化application里配置的参数
public NacosConfigProperties nacosConfigProperties() {
return new NacosConfigProperties();
}
@Bean
@ConditionalOnMissingBean
//初始化一个NacosConfigManager
public NacosConfigManager nacosConfigManager(
NacosConfigProperties nacosConfigProperties) {
return new NacosConfigManager(nacosConfigProperties);
}
@Bean
//只是初始化一个NacosPropertySourceLocator
public NacosPropertySourceLocator nacosPropertySourceLocator(
NacosConfigManager nacosConfigManager) {
return new NacosPropertySourceLocator(nacosConfigManager);
}
}
- nacosConfigProperties方法,主要是对参数进行一些处理
private void overrideFromEnv() {
if (StringUtils.isEmpty(this.getServerAddr())) {
String serverAddr = environment
.resolvePlaceholders("${spring.cloud.nacos.config.server-addr:}");
if (StringUtils.isEmpty(serverAddr)) {
serverAddr = environment.resolvePlaceholders(
"${spring.cloud.nacos.server-addr:localhost:8848}");
}
this.setServerAddr(serverAddr);
}
if (StringUtils.isEmpty(this.getUsername())) {
this.setUsername(
environment.resolvePlaceholders("${spring.cloud.nacos.username:}"));
}
if (StringUtils.isEmpty(this.getPassword())) {
this.setPassword(
environment.resolvePlaceholders("${spring.cloud.nacos.password:}"));
}
}
- nacosConfigManager方法,主要是创建一个nacosConfigManager
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;
}
这边主要调用
com.alibaba.nacos.client.config.NacosConfigService#NacosConfigService方法来(后面详解)
- NacosPropertySourceLocator主要是创建一个NacosPropertySourceLocator
public NacosPropertySourceLocator(NacosConfigManager nacosConfigManager) {
this.nacosConfigManager = nacosConfigManager;
this.nacosConfigProperties = nacosConfigManager.getNacosConfigProperties();
}
注册listener
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
// many Spring context
if (this.ready.compareAndSet(false, true)) {
this.registerNacosListenersForApplications();
}
}
/**
* register Nacos Listeners.
*/
private void registerNacosListenersForApplications() {
if (isRefreshEnabled()) {
for (NacosPropertySource propertySource : NacosPropertySourceRepository
.getAll()) {
if (!propertySource.isRefreshable()) {
continue;
}
String dataId = propertySource.getDataId();
registerNacosListener(propertySource.getGroup(), dataId);
}
}
}
当获取配置的时候主要是通过NacosPropertySourceLocator#locate来获取(参考spring的配置规则)
@Override
public PropertySource> locate(Environment env) {
//env里面主要是springboot的环境配置,包含本地配置active-profile等
nacosConfigProperties.setEnvironment(env);
//获取nacos-config配置
ConfigService configService = nacosConfigManager.getConfigService();
if (null == configService) {
log.warn("no instance of config service found, can't load config from nacos");
return null;
}
//获取配置的超时时间
long timeout = nacosConfigProperties.getTimeout();
nacosPropertySourceBuilder = new NacosPropertySourceBuilder(configService,
timeout);
String name = nacosConfigProperties.getName();
String dataIdPrefix = nacosConfigProperties.getPrefix();
if (StringUtils.isEmpty(dataIdPrefix)) {
dataIdPrefix = 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;
}
这里正式开始加载配置,而加载顺序也可以看出,共享配置 ->扩展配置 -> 当前应用配置,
当后面加载有相同配置的时候,直接覆盖之前的配置。共享跟扩展设置值set的方法已经废弃不用了。
也就是配置的优先级为 当前应用配置>扩展配置->共享配置
- 加载共享配置(已经@Deprecated废弃掉了)
private void loadSharedConfiguration(
CompositePropertySource compositePropertySource) {
List sharedConfigs = nacosConfigProperties
.getSharedConfigs();
if (!CollectionUtils.isEmpty(sharedConfigs)) {
checkConfiguration(sharedConfigs, "shared-configs");
loadNacosConfiguration(compositePropertySource, sharedConfigs);
}
}
- 加载共享配置(已经@Deprecated废弃掉了)
private void loadExtConfiguration(CompositePropertySource compositePropertySource) {
List extConfigs = nacosConfigProperties
.getExtensionConfigs();
if (!CollectionUtils.isEmpty(extConfigs)) {
checkConfiguration(extConfigs, "extension-configs");
loadNacosConfiguration(compositePropertySource, extConfigs);
}
}
- 加载当前应用配置
private void loadApplicationConfiguration(
CompositePropertySource compositePropertySource, String dataIdPrefix,
NacosConfigProperties properties, Environment environment) {
String fileExtension = properties.getFileExtension();
String nacosGroup = properties.getGroup();
// load directly once by default
// 直接根据spring.cloud.nacos.config.name配置名称去nacos取,
// 不带后缀(例如:mall-portal-config)
loadNacosDataIfPresent(compositePropertySource, dataIdPrefix, nacosGroup,
fileExtension, true);
// load with suffix, which have a higher priority than the default
// 根据配置配置+后缀去取,优先级高于上面默认的(例如:mall-portal-config.yml)
loadNacosDataIfPresent(compositePropertySource,
dataIdPrefix + DOT + fileExtension, nacosGroup, fileExtension, true);
// Loaded with profile, which have a higher priority than the suffix
// 根据profile去取(dev,test等),优先级最高(例如:mall-portal-config-dev.yml)
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;
}
//获取nacos配置并包装成NacosPropertySource
NacosPropertySource propertySource = this.loadNacosPropertySource(dataId, group,
fileExtension, isRefreshable);
this.addFirstPropertySource(composite, propertySource, false);
}
//----------------------------------✂------------------------------------------
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);
}
//----------------------------------✂------------------------------------------
NacosPropertySource build(String dataId, String group, String fileExtension,
boolean isRefreshable) {
//主要是通过这个方法来从nacos服务端获取数据
Map p = loadNacosData(dataId, group, fileExtension);
NacosPropertySource nacosPropertySource = new NacosPropertySource(group, dataId,
p, new Date(), isRefreshable);
//更新本地缓存
NacosPropertySourceRepository.collectNacosPropertySource(nacosPropertySource);
return nacosPropertySource;
}
//----------------------------------✂------------------------------------------
private Map loadNacosData(String dataId, String group,
String fileExtension) {
String data = null;
try {
//从service端获取数据
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结构
Map 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;
}
- 至此nacos config拉取配置中心的代码就完成了,下面的更换配置等属于springboot的内容了,具体nacos config如何工作可以查看nacos config源码分析
Nacos配置中心源码