Spring Cloud Alibaba Nacos Config加載配置詳解流程
1、加載節(jié)點(diǎn)
SpringBoot啟動(dòng)時(shí),會(huì)執(zhí)行這個(gè)方法:SpringApplication#run,這個(gè)方法中會(huì)調(diào)prepareContext來準(zhǔn)備上下文,這個(gè)方法中調(diào)用了applyInitializers方法來執(zhí)行實(shí)現(xiàn)了ApplicationContextInitializer接口的類的initialize方法。其中包括PropertySourceBootstrapConfiguration#initialize 來加載外部的配置。
@Autowired(required = false)
private List<PropertySourceLocator> propertySourceLocators = new ArrayList<>();
public void initialize(ConfigurableApplicationContext applicationContext) {
...
for (PropertySourceLocator locator : this.propertySourceLocators) {
//載入PropertySource
Collection<PropertySource<?>> source = locator.locateCollection(environment);
...
}
...
}
//org.springframework.cloud.bootstrap.config.PropertySourceLocator#locateCollection
default Collection<PropertySource<?>> locateCollection(Environment environment) {
return locateCollection(this, environment);
}
//org.springframework.cloud.bootstrap.config.PropertySourceLocator#locateCollection
static Collection<PropertySource<?>> locateCollection(PropertySourceLocator locator, Environment environment) {
//執(zhí)行l(wèi)ocate方法,將PropertySource加載進(jìn)來
PropertySource<?> propertySource = locator.locate(environment);
...
}這個(gè)類中會(huì)注入實(shí)現(xiàn)了PropertySourceLocator接口的類,在nacos中是NacosPropertySourceLocator。
在initialize方法中會(huì)執(zhí)行NacosPropertySourceLocator的locate方法,將NacosPropertySource加載進(jìn)來。
2、NacosPropertySourceLocator的注冊
NacosPropertySourceLocator在配置類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);
}
}在這里會(huì)依次注冊NacosConfigProperties,NacosConfigManager,NacosPropertySourceLocator。
3、加載
//com.alibaba.cloud.nacos.client.NacosPropertySourceLocator#locate
public PropertySource<?> locate(Environment env) {
nacosConfigProperties.setEnvironment(env);
//獲取ConfigService
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();
//構(gòu)建nacosPropertySourceBuilder
nacosPropertySourceBuilder = new NacosPropertySourceBuilder(configService,
timeout);
String name = nacosConfigProperties.getName();
//獲取dataIdPrefix,一次從prefix,name,spring.application.name中獲取
String dataIdPrefix = nacosConfigProperties.getPrefix();
if (StringUtils.isEmpty(dataIdPrefix)) {
dataIdPrefix = name;
}
if (StringUtils.isEmpty(dataIdPrefix)) {
dataIdPrefix = env.getProperty("spring.application.name");
}
//構(gòu)建CompositePropertySource:NACOS
CompositePropertySource composite = new CompositePropertySource(
NACOS_PROPERTY_SOURCE_NAME);
//加載share 配置
loadSharedConfiguration(composite);
//加載extention 配置
loadExtConfiguration(composite);
//加載application配置
loadApplicationConfiguration(composite, dataIdPrefix, nacosConfigProperties, env);
return composite;
}3.1、加載share
private void loadSharedConfiguration(
CompositePropertySource compositePropertySource) {
//獲取share節(jié)點(diǎn)的配置信息
List<NacosConfigProperties.Config> sharedConfigs = nacosConfigProperties
.getSharedConfigs();
//如果不為空,加載
if (!CollectionUtils.isEmpty(sharedConfigs)) {
checkConfiguration(sharedConfigs, "shared-configs");
loadNacosConfiguration(compositePropertySource, sharedConfigs);
}
}
加載配置,公用方法
//com.alibaba.cloud.nacos.client.NacosPropertySourceLocator#loadNacosConfiguration
private void loadNacosConfiguration(final CompositePropertySource composite,
List<NacosConfigProperties.Config> configs) {
for (NacosConfigProperties.Config config : configs) {
loadNacosDataIfPresent(composite, config.getDataId(), config.getGroup(),
NacosDataParserHandler.getInstance()
.getFileExtension(config.getDataId()),
config.isRefresh());
}
}
//com.alibaba.cloud.nacos.client.NacosPropertySourceLocator#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,后面也會(huì)用這個(gè)方法
NacosPropertySource propertySource = this.loadNacosPropertySource(dataId, group,
fileExtension, isRefreshable);
//將NacosPropertySource放入第一個(gè)
this.addFirstPropertySource(composite, propertySource, false);
}
加載NacosPropertySource
//com.alibaba.cloud.nacos.client.NacosPropertySourceLocator#loadNacosPropertySource
private NacosPropertySource loadNacosPropertySource(final String dataId,
final String group, String fileExtension, boolean isRefreshable) {
//標(biāo)注@RefreshScope的類的數(shù)量不為0,且不允許自動(dòng)刷新,從緩存加載nacos的配置
if (NacosContextRefresher.getRefreshCount() != 0) {
if (!isRefreshable) {
return NacosPropertySourceRepository.getNacosPropertySource(dataId,
group);
}
}
return nacosPropertySourceBuilder.build(dataId, group, fileExtension,
isRefreshable);
}
從緩存中加載
//NacosPropertySourceRepository
private final static ConcurrentHashMap<String, NacosPropertySource> NACOS_PROPERTY_SOURCE_REPOSITORY = new ConcurrentHashMap<>();
public static NacosPropertySource getNacosPropertySource(String dataId,
String group) {
return NACOS_PROPERTY_SOURCE_REPOSITORY.get(getMapKey(dataId, group));
}
public static String getMapKey(String dataId, String group) {
return String.join(NacosConfigProperties.COMMAS, String.valueOf(dataId),
String.valueOf(group));
}NacosPropertySourceRepository中緩存了NacosPropertySource
獲取配置并加入緩存
//com.alibaba.cloud.nacos.client.NacosPropertySourceBuilder#build
NacosPropertySource build(String dataId, String group, String fileExtension,
boolean isRefreshable) {
//獲取配置
List<PropertySource<?>> propertySources = loadNacosData(dataId, group,
fileExtension);
//包裝成NacosPropertySource
NacosPropertySource nacosPropertySource = new NacosPropertySource(propertySources,
group, dataId, new Date(), isRefreshable);
//加入緩存
NacosPropertySourceRepository.collectNacosPropertySource(nacosPropertySource);
return nacosPropertySource;
}//com.alibaba.cloud.nacos.client.NacosPropertySourceBuilder#loadNacosData
private List<PropertySource<?>> 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 Collections.emptyList();
}
if (log.isDebugEnabled()) {
log.debug(String.format(
"Loading nacos data, dataId: '%s', group: '%s', data: %s", dataId,
group, data));
}
return NacosDataParserHandler.getInstance().parseNacosData(dataId, data,
fileExtension);
}
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 Collections.emptyList();
}//com.alibaba.nacos.client.config.NacosConfigService#getConfig
public String getConfig(String dataId, String group, long timeoutMs) throws NacosException {
return getConfigInner(namespace, dataId, group, timeoutMs);
}
//com.alibaba.nacos.client.config.NacosConfigService#getConfigInner
private String getConfigInner(String tenant, String dataId, String group, long timeoutMs) throws NacosException {
group = blank2defaultGroup(group);
ParamUtils.checkKeyParam(dataId, group);
//聲明響應(yīng)
ConfigResponse cr = new ConfigResponse();
//設(shè)置dataId,tenant,group
cr.setDataId(dataId);
cr.setTenant(tenant);
cr.setGroup(group);
// use local config first
//先從本地緩存中加載
String content = LocalConfigInfoProcessor.getFailover(worker.getAgentName(), dataId, group, tenant);
if (content != null) {
LOGGER.warn("[{}] [get-config] get failover ok, dataId={}, group={}, tenant={}, config={}",
worker.getAgentName(), dataId, group, tenant, ContentUtils.truncateContent(content));
cr.setContent(content);
String encryptedDataKey = LocalEncryptedDataKeyProcessor
.getEncryptDataKeyFailover(agent.getName(), dataId, group, tenant);
cr.setEncryptedDataKey(encryptedDataKey);
configFilterChainManager.doFilter(null, cr);
content = cr.getContent();
return content;
}
try {
//從服務(wù)器獲取配置
ConfigResponse response = worker.getServerConfig(dataId, group, tenant, timeoutMs, false);
cr.setContent(response.getContent());
cr.setEncryptedDataKey(response.getEncryptedDataKey());
configFilterChainManager.doFilter(null, cr);
content = cr.getContent();
return content;
} catch (NacosException ioe) {
if (NacosException.NO_RIGHT == ioe.getErrCode()) {
throw ioe;
}
LOGGER.warn("[{}] [get-config] get from server error, dataId={}, group={}, tenant={}, msg={}",
worker.getAgentName(), dataId, group, tenant, ioe.toString());
}
LOGGER.warn("[{}] [get-config] get snapshot ok, dataId={}, group={}, tenant={}, config={}",
worker.getAgentName(), dataId, group, tenant, ContentUtils.truncateContent(content));
content = LocalConfigInfoProcessor.getSnapshot(worker.getAgentName(), dataId, group, tenant);
cr.setContent(content);
String encryptedDataKey = LocalEncryptedDataKeyProcessor
.getEncryptDataKeyFailover(agent.getName(), dataId, group, tenant);
cr.setEncryptedDataKey(encryptedDataKey);
configFilterChainManager.doFilter(null, cr);
content = cr.getContent();
return content;
}將NacosPropertySource加入composite的第一個(gè)
//com.alibaba.cloud.nacos.client.NacosPropertySourceLocator#addFirstPropertySource
private void addFirstPropertySource(final CompositePropertySource composite,
NacosPropertySource nacosPropertySource, boolean ignoreEmpty) {
if (null == nacosPropertySource || null == composite) {
return;
}
if (ignoreEmpty && nacosPropertySource.getSource().isEmpty()) {
return;
}
composite.addFirstPropertySource(nacosPropertySource);
}
3.2、加載extention
private void loadExtConfiguration(CompositePropertySource compositePropertySource) {
//獲取extention節(jié)點(diǎn)的配置信息
List<NacosConfigProperties.Config> extConfigs = nacosConfigProperties
.getExtensionConfigs();
//如果不為空,加載
if (!CollectionUtils.isEmpty(extConfigs)) {
checkConfiguration(extConfigs, "extension-configs");
loadNacosConfiguration(compositePropertySource, extConfigs);
}
}
3.3、加載主配置文件
private void loadApplicationConfiguration(
CompositePropertySource compositePropertySource, String dataIdPrefix,
NacosConfigProperties properties, Environment environment) {
//獲取文件擴(kuò)展名
String fileExtension = properties.getFileExtension();
//獲取group
String nacosGroup = properties.getGroup();
// load directly once by default
//加載nacos的配置
loadNacosDataIfPresent(compositePropertySource, dataIdPrefix, nacosGroup,
fileExtension, true);
// load with suffix, which have a higher priority than the default
//加載帶后綴的配置,優(yōu)先級高于上一個(gè)
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;
//加載帶profile,文件格式后綴的配置,優(yōu)先級高于上一個(gè)
loadNacosDataIfPresent(compositePropertySource, dataId, nacosGroup,
fileExtension, true);
}
}這里會(huì)加載至少三個(gè)nacos上面的配置文件,按優(yōu)先級依次為application,application.yaml,application-dev.yaml。
到此這篇關(guān)于Spring Cloud Alibaba Nacos Config加載配置詳解流程的文章就介紹到這了,更多相關(guān)Spring Cloud Alibaba Nacos Config 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring?MVC中@Controller和@RequestMapping注解使用
這篇文章主要介紹了Spring?MVC中@Controller和@RequestMapping注解使用,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02
spring @Scheduled注解的使用誤區(qū)及解決
這篇文章主要介紹了spring @Scheduled注解的使用誤區(qū)及解決,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11
淺談@RequestParam 參數(shù)是否必須傳的問題
這篇文章主要介紹了淺談@RequestParam 參數(shù)是否必須傳的問題,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-02-02
JavaWeb實(shí)現(xiàn)學(xué)生管理系統(tǒng)的超詳細(xì)過程
學(xué)生信息管理系統(tǒng)是針對學(xué)校人事處的大量業(yè)務(wù)處理工作而開發(fā)的管理軟件,主要用于學(xué)校學(xué)生信息管理,下面這篇文章主要給大家介紹了關(guān)于JavaWeb實(shí)現(xiàn)學(xué)生管理系統(tǒng)的超詳細(xì)過程,需要的朋友可以參考下2023-05-05

