本文收录于专栏 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 Bean
registerGlobalNacosProperties(attributes, registry, environment,
CONFIG_GLOBAL_NACOS_PROPERTIES_BEAN_NAME);
// Register Nacos Common Beans
registerNacosCommonBeans(registry);
// Register Nacos Config Beans
registerNacosConfigBeans(registry, environment, beanFactory);
// Invoke NacosPropertySourcePostProcessor immediately
// in order to enhance the precedence of @NacosPropertySource process
invokeNacosPropertySourcePostProcessor(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 possible
List<NacosPropertySource> nacosPropertySources = buildNacosPropertySources(
beanName, beanDefinition);
// Add Orderly
for (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
注解为字段赋值的流程。