本文收錄于專欄 Nacos
推薦閱讀:Nacos 架構 & 原理
文章目錄
- 前言
- 一、NacosConfigBeanDefinitionRegistrar
- 二、NacosPropertySourcePostProcessor
- 三、AbstractNacosPropertySourceBuilder
- 總結「AI生成」
前言
專欄前幾篇文章主要講了Nacos作為服務注冊中心相關的代碼,本章開始梳理Nacos作為配置中心來使用時相關部分的代碼主要邏輯。
??:使用的Nacos版本為2.3.X
??:springboot集成Nacos
<dependency><groupId>com.alibaba.boot</groupId><artifactId>nacos-config-spring-boot-starter</artifactId><version>0.2.12</version></dependency>
一、NacosConfigBeanDefinitionRegistrar
直接從spring.factories中找和心類:NacosConfigAutoConfiguration
@ConditionalOnProperty(name = NacosConfigConstants.ENABLED, matchIfMissing = true)
@ConditionalOnMissingBean(name = CONFIG_GLOBAL_NACOS_PROPERTIES_BEAN_NAME)
@EnableConfigurationProperties(value = NacosConfigProperties.class)
@ConditionalOnClass(name = "org.springframework.boot.context.properties.bind.Binder")
@Import(value = { NacosConfigBootBeanDefinitionRegistrar.class })
@EnableNacosConfig
public class NacosConfigAutoConfiguration {
}
可以看到這個類里啥也沒有,核心就是引入@EnableNacosConfig
@Target({ ElementType.TYPE, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(NacosConfigBeanDefinitionRegistrar.class)
public @interface EnableNacosConfig {//一些配置文件中nacos配置的占位符定義
}
我們來看關鍵類:NacosConfigBeanDefinitionRegistrar
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {AnnotationAttributes attributes = fromMap(metadata.getAnnotationAttributes(EnableNacosConfig.class.getName()));// Register Global Nacos Properties BeanregisterGlobalNacosProperties(attributes, registry, environment,CONFIG_GLOBAL_NACOS_PROPERTIES_BEAN_NAME);// Register Nacos Common BeansregisterNacosCommonBeans(registry);// Register Nacos Config BeansregisterNacosConfigBeans(registry, environment, beanFactory);// Invoke NacosPropertySourcePostProcessor immediately// in order to enhance the precedence of @NacosPropertySource processinvokeNacosPropertySourcePostProcessor(beanFactory);
}
invokeNacosPropertySourcePostProcessor
上邊的代碼處理的都是和spring集成相關的邏輯,我們這里暫不去展開。invokeNacosPropertySourcePostProcessor
通過這個名字我們可以簡單看出來,這個方法處理的是post processor
,一些后置邏輯。
二、NacosPropertySourcePostProcessor
public static void invokeNacosPropertySourcePostProcessor(BeanFactory beanFactory) {NacosPropertySourcePostProcessor postProcessor = beanFactory.getBean(//"nacosPropertySourcePostProcessor"NacosPropertySourcePostProcessor.BEAN_NAME,NacosPropertySourcePostProcessor.class);postProcessor.postProcessBeanFactory((ConfigurableListableBeanFactory) beanFactory);
}
代碼看到這里我們做個暫停,再次想一想我們到底要找什么,有了明確的方向才不至于在洋洋灑灑的源碼里一頭霧水。
那么,我們到底要找什么?
🏹對!我們想要知道的是Nacos是如何給@NacosValue標注的字段賦值的。🏹
然后再來看代碼,是否就清晰了一點?
🏹對!代碼中的關鍵詞就是process
🏹
那就繼續看postProcessor.postProcessBeanFactory((ConfigurableListableBeanFactory) beanFactory);
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)throws BeansException {String[] abstractNacosPropertySourceBuilderBeanNames = BeanUtils.getBeanNames(beanFactory, AbstractNacosPropertySourceBuilder.class);this.nacosPropertySourceBuilders = new ArrayList<AbstractNacosPropertySourceBuilder>(abstractNacosPropertySourceBuilderBeanNames.length);for (String beanName : abstractNacosPropertySourceBuilderBeanNames) {this.nacosPropertySourceBuilders.add(beanFactory.getBean(beanName,AbstractNacosPropertySourceBuilder.class));}NacosPropertySourcePostProcessor.beanFactory = beanFactory;this.configServiceBeanBuilder = getConfigServiceBeanBuilder(beanFactory);String[] beanNames = beanFactory.getBeanDefinitionNames();for (String beanName : beanNames) {processPropertySource(beanName, beanFactory);}
}
有了關鍵詞,那我們就繼續看processPropertySource(beanName, beanFactory);
private void processPropertySource(String beanName,ConfigurableListableBeanFactory beanFactory) {//processedBeanNames記錄的是已經處理過的beanName, 看下這個方法的最后一行就知道了。if (processedBeanNames.contains(beanName)) {return;}//BeanDefinition:通過spring factory獲取的bean定義類BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);// Build multiple instance if possibleList<NacosPropertySource> nacosPropertySources = buildNacosPropertySources(beanName, beanDefinition);// Add Orderlyfor (NacosPropertySource nacosPropertySource : nacosPropertySources) {addNacosPropertySource(nacosPropertySource);Properties properties = configServiceBeanBuilder.resolveProperties(nacosPropertySource.getAttributesMetadata());addListenerIfAutoRefreshed(nacosPropertySource, properties, environment);}processedBeanNames.add(beanName);
}private List<NacosPropertySource> buildNacosPropertySources(String beanName,BeanDefinition beanDefinition) {for (AbstractNacosPropertySourceBuilder builder : nacosPropertySourceBuilders) {if (builder.supports(beanDefinition)) {return builder.build(beanName, beanDefinition);}}return Collections.emptyList();
}
builder.build(beanName, beanDefinition)
我們來著重看下這個方法
三、AbstractNacosPropertySourceBuilder
我們看下這個build方法的具體實現
public List<NacosPropertySource> build(String beanName, T beanDefinition) {Map<String, Object>[] attributesArray = resolveRuntimeAttributesArray(beanDefinition, globalNacosProperties);int size = attributesArray == null ? 0 : attributesArray.length;if (size == 0) {return Collections.emptyList();}List<NacosPropertySource> nacosPropertySources = new ArrayList<NacosPropertySource>(size);for (int i = 0; i < size; i++) {Map<String, Object> attributes = attributesArray[i];if (!CollectionUtils.isEmpty(attributes)) {NacosPropertySource nacosPropertySource = doBuild(beanName,beanDefinition, attributesArray[i]);NacosConfigMetadataEvent metadataEvent = createMetaEvent(nacosPropertySource, beanDefinition);initMetadataEvent(nacosPropertySource, beanDefinition, metadataEvent);publishMetadataEvent(metadataEvent);nacosPropertySources.add(nacosPropertySource);}}return nacosPropertySources;
}
這個方法返回的是List<NacosPropertySource>
,而NacosPropertySource
是在doBuild(beanName,beanDefinition, attributesArray[i]);
這里給出的,那就著重看下這里的實現。
ps: 篇幅限制,只給出重要代碼
protected NacosPropertySource doBuild(String beanName, T beanDefinition,Map<String, Object> runtimeAttributes) {//。。。String nacosConfig = nacosConfigLoader.load(dataId, groupId, nacosProperties);//。。。NacosPropertySource nacosPropertySource = new NacosPropertySource(dataId, groupId,name, nacosConfig, type);//。。。
}
NacosPropertySource中存放配置的位置:
這個方法主要做了兩件事:
- 獲取
nacosConfig
。我們知道,Nacos客戶端在第一次獲取數據的時候會去server端全量拉取一次,那所以這里肯定有和服務端交互的邏輯。 - 生成
NacosPropertySource
:這是doBuild要返回的數據。
總結「AI生成」
本文分析了Nacos客戶端源碼,重點探討了Nacos作為配置中心的主要邏輯。以下是關鍵類的描述和功能總結:
-
NacosConfigAutoConfiguration:
- 位于Spring的
spring.factories
中,負責自動配置Nacos。雖然類本身不包含邏輯,但通過@EnableNacosConfig
注解引入了Nacos配置的核心類NacosConfigBeanDefinitionRegistrar
。
- 位于Spring的
-
NacosConfigBeanDefinitionRegistrar:
- 負責注冊Nacos的相關Bean定義。主要方法
registerBeanDefinitions
包括注冊全局Nacos屬性、Nacos通用Bean和Nacos配置Bean,并調用NacosPropertySourcePostProcessor
處理后置邏輯。
- 負責注冊Nacos的相關Bean定義。主要方法
-
NacosPropertySourcePostProcessor:
- 通過
invokeNacosPropertySourcePostProcessor
方法調用,負責后置處理邏輯,主要處理配置源的加載和Nacos屬性的解析。其核心方法postProcessBeanFactory
遍歷所有Bean定義并處理Nacos配置源。
- 通過
-
AbstractNacosPropertySourceBuilder:
- 通過
build
方法構建NacosPropertySource
。該方法首先解析運行時屬性數組,然后根據屬性構建Nacos配置源。核心方法doBuild
實現了與Nacos服務端的交互,獲取配置數據并生成NacosPropertySource
。
- 通過
-
NacosPropertySource:
- 存放從Nacos服務端獲取的配置數據,是Nacos配置管理的核心數據結構。
本文通過深入分析這些關鍵類和方法,揭示了Nacos客戶端在作為配置中心時的工作機制,特別是如何通過@NacosValue
注解為字段賦值的流程。