本文收录于专栏 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
注解为字段赋值的流程。