Nacos配置中心客户端源码分析(一): 客户端如何初始化配置

本文收录于专栏 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中存放配置的位置:在这里插入图片描述

这个方法主要做了两件事:

  1. 获取 nacosConfig。我们知道,Nacos客户端在第一次获取数据的时候会去server端全量拉取一次,那所以这里肯定有和服务端交互的逻辑。
  2. 生成NacosPropertySource:这是doBuild要返回的数据。

总结「AI生成」

本文分析了Nacos客户端源码,重点探讨了Nacos作为配置中心的主要逻辑。以下是关键类的描述和功能总结:

  1. NacosConfigAutoConfiguration

    • 位于Spring的spring.factories中,负责自动配置Nacos。虽然类本身不包含逻辑,但通过@EnableNacosConfig注解引入了Nacos配置的核心类NacosConfigBeanDefinitionRegistrar
  2. NacosConfigBeanDefinitionRegistrar

    • 负责注册Nacos的相关Bean定义。主要方法registerBeanDefinitions包括注册全局Nacos属性、Nacos通用Bean和Nacos配置Bean,并调用NacosPropertySourcePostProcessor处理后置逻辑。
  3. NacosPropertySourcePostProcessor

    • 通过invokeNacosPropertySourcePostProcessor方法调用,负责后置处理逻辑,主要处理配置源的加载和Nacos属性的解析。其核心方法postProcessBeanFactory遍历所有Bean定义并处理Nacos配置源。
  4. AbstractNacosPropertySourceBuilder

    • 通过build方法构建NacosPropertySource。该方法首先解析运行时属性数组,然后根据属性构建Nacos配置源。核心方法doBuild实现了与Nacos服务端的交互,获取配置数据并生成NacosPropertySource
  5. NacosPropertySource

    • 存放从Nacos服务端获取的配置数据,是Nacos配置管理的核心数据结构。

本文通过深入分析这些关键类和方法,揭示了Nacos客户端在作为配置中心时的工作机制,特别是如何通过@NacosValue注解为字段赋值的流程。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/760018.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

github主页这样优化,让人眼前一亮

我的主页&#xff08;一之十六&#xff09; 1. 创建与账户ID同名的仓库 注意&#xff1a;记得勾选Add a README file 2. markdown语法自定义README.md 3. 辅助工具 优秀profile&#xff1a;https://zzetao.github.io/awesome-github-profile/动态文字&#xff1a;https://r…

SpringMVC(1)——入门程序+流程分析

MVC都是哪三层&#xff1f;在Spring里面分别对应什么&#xff1f;SpringMVC的架构是什么&#xff1f; 我们使用Spring开发JavaWeb项目&#xff0c;一般都是BS架构&#xff0c;也就是Browser&#xff08;浏览器&#xff09;-Server&#xff08;服务器&#xff09;架构 这种架构…

谷歌开发者新号上架攻略:开发者实战经验分享

前段时间&#xff0c;不少开发者朋友们在纷纷在吐槽新账号没法上架成功。以前谷歌对新号是真的很严格&#xff0c;但现在情况似乎有所好转。 今天&#xff0c;和大家聊聊如何在新号成功上架上“快人一步”&#xff0c;以及怎样增加账号权重提高上架成功率。 首先&#xff0c;我…

成绩发布背后:老师的无奈与痛点

在教育的广阔天地里&#xff0c;教师这一角色承载着无数的期望与责任。他们不仅是知识的传播者&#xff0c;更是学生心灵的引路人。而对于班主任老师来说&#xff0c;他们的角色更加多元&#xff0c;他们不仅是老师&#xff0c;还必须是“妈妈”。除了像其他老师一样备课、上课…

Linux文件系统与设备文件

一、Linux文件操作 Linux的文件系统API主要涉及创建、打开、读写、定位、关闭文件 创建 int creat(const char *filename, mode_t mode);mode: 代表新建文件的存取权限&#xff0c;需要和umask相与才能确定最终权限(mode&umask)。 umask代表文件在创建时需要去掉的存取…

8.12 矢量图层面要素单一符号使用十(箭头线渲染边界)

前言 本章介绍矢量图层线要素单一符号中箭头线渲染边界的使用说明&#xff1a;文章中的示例代码均来自开源项目qgis_cpp_api_apps 箭头线渲染边界&#xff08;Outline: Arrow&#xff09; Outline系列只画边界&#xff0c;不填充内容以protected_areas.shp为例&#xff0c;图…

Spring 动态增强逻辑执行分析

1、假如UserService中存在被增强的public 普通方法&#xff0c;那么spring ioc时就会创建对应的代理对象放置到容器中&#xff1b; 2、那么Controller中注入的userService就是代理对象&#xff1b; Service public class UserService {Transactionalpublic void f2(String us…

【训练篇】MLU370-M8 完成 qwen1.5-7b-chat-lora训练及推理

文章目录 前言一、平台环境配置二、环境 or 模型准备1.模型下载2.环境准备2.1 modelscope2.2 transformers2.3 accelerate2.4 deepspeed2.5 peft2.6 环境代码修改 3训练代码准备4 代码修改 三&#xff0c;训练后推理验证四.推理效果展示1.微调前2.微调后 前言 本期我们采用魔塔…

【雷达原理】雷达测角原理及实现方法

目录 一、雷达测角原理1.1 测角研究历史和现状1.2 测角方法总结1.3 3DFFT测角1.3.1 基本原理1.2.2 测角性能 二、MATLAB仿真案例参考文献 一、雷达测角原理 1.1 测角研究历史和现状 &#xff08;1&#xff09;早期采用窄波束对准目标&#xff0c;目标的角度对应于天线的角度读…

【高性能服务器】服务器概述

&#x1f525;博客主页&#xff1a; 我要成为C领域大神&#x1f3a5;系列专栏&#xff1a;【C核心编程】 【计算机网络】 【Linux编程】 【操作系统】 ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 本博客致力于知识分享&#xff0c;与更多的人进行学习交流 ​ 服务器概述 服…

[深入理解DDR] 总目录

依公知及经验整理&#xff0c;原创保护&#xff0c;禁止转载。 专栏 《深入理解DDR》 蓝色的是传送门&#xff0c;点击链接即可到达指定文章。 图。 DDR 分类 导论 [RAM] DRAM 导论&#xff1a;DDR4 | DDR5 | LPDDR5 | GDRR6 | HBM 应运而生 运存与内存&#xff1f;内存与存…

Golang-GMP

GMP调度 golang-GMP语雀笔记整理 GMP调度设计目的&#xff0c;为何设计GMP?GMP的底层实现几个核心数据结构GMP调度流程 设计目的&#xff0c;为何设计GMP? 无论是多进程、多线程目的都是为了并发提高cpu的利用率&#xff0c;但多进程、多线程都存在局限性。比如多进程通过时…

第三方服务提供商的五大风险

亚马逊如何应对网络安全挑战 关键网络安全统计数据和趋势 移动优先世界中安全和隐私策略 当今数字时代网络安全的重要性 用户无法停止犯安全错误的 3 个原因 首席安全官可能过于依赖 EDR/XDR 防御 随着业务流程变得越来越复杂&#xff0c;公司开始转向第三方来提高其提供关…

Unity动画系统(1)

6.1 动画系统基础1-5_哔哩哔哩_bilibili 模型信息 Generic非人型 Configure 虚线圈可以没有&#xff0c;实线圈必须有&#xff0c;15个骨骼是必须的 p313 尾巴、翅膀属于非人型 p314 一般使用create from this model 游戏对象不再旋转 游戏对象不再发生位移 调整中心位置

WP黑格导航主题BlackCandy

BlackCandy-V2.0全新升级&#xff01;首推专题区(推荐分类)更多自定义颜色&#xff01;选择自己喜欢的色系&#xff0c;焕然一新的UI设计&#xff0c;更加扁平和现代化&#xff01; WP黑格导航主题BlackCandy

springcloud第4季 分布式事务seata实现AT模式案例2【经典案例】

一 seata案例 1.1 背景说明 本案例使用seata的at模式&#xff0c;模拟分布式事务场景&#xff1a;【下订单&#xff0c;减库存&#xff0c;扣余额&#xff0c;改状态】 AT模式原理&#xff1a;是2pc方案的演变&#xff0c; 一阶段&#xff1a;业务数据和回滚日志记录在同一…

kubernetes中使用Helm搭建Redis集群

1. 环境要求 首先需要有kubernetes集群环境&#xff0c;搭建kubernetes集群可以使用kubeSphere、kubespray等工具安装集群。其次要安装helm&#xff0c;并且添加了可用的Chart仓库 2. 确认合适的Chart包 helm官网上搜索redis 找到Star数多的Chart包&#xff0c;一类是redis包…

JVM(12):虚拟机性能分析和故障解决工具之JConsole

1 JConsole作用 查看Java应用程序的运行概况&#xff0c;监视垃圾收集器管理的虚拟机内存(堆和元空间)的变化趋势&#xff0c;以及监控程序内的线程。 2 使用说明 代码如下&#xff1a; package com.example.demo;import java.io.IOException; import java.util.ArrayList; …

04.Ambari自定义服务开发-自定义服务配置文件在Ambari中的设置方法

文章目录 设置方法配置文件设置Custom xxx配置文件详细的配置方法.xml文件的整体格式基础参数格式value-attributes配置介绍设置属性在服务安装后不可修改设置允许字段为空是否显示配置名称参数类型设置字符串类型PasswordBooleanIntFloatDirectoryDirectoriesContent-多行文本…

2024 vue3入门教程:windows系统下部署node环境

一、打开下载的node官网 Node.js — 下载 Node.js 二、根据个人喜好的下载方法&#xff0c;下载到自己的电脑盘符下 三、我用的是方法3下载的压缩包&#xff0c;解压到E盘nodejs目录下&#xff08;看个人&#xff09; 四、配置电脑的环境变量&#xff0c;新建环境变量的时候…