1.解+源码讲解 Nacos 客户端动态监听配置机制
2.Spring 中无处不在的源码 Properties
3.Nacos系列如何通过Nacos拉取和自动刷新Spring Boot配置?
4.springboot读取配置(springboot读取配置文件)
5.SpringBoot全局配置文件加载原理详解(万字源码分析)
解+源码讲解 Nacos 客户端动态监听配置机制
图解+源码讲解 Nacos 客户端动态监听配置机制
在人生中第一要紧的是发现自己。为了这个目的源码,各位时常需要孤独和深思 —— 南森 Nacos 源码分析系列相关文章
从零开始看 Nacos 源码环境搭建
图解+源码讲解 Nacos 客户端发起注册流程
图解+源码讲解 Nacos 服务端处理注册请求逻辑
图解+源码讲解 Nacos 客户端下线流程
图解+源码讲解 Nacos 服务端处理下线请求
图解+源码讲解 Nacos 客户端发起心跳请求
图解+源码讲解 Nacos 服务端处理心跳请求
图解+源码讲解 Nacos 服务端处理配置获取请求
图解+源码讲解 Nacos 客户端动态监听配置机制
NacosConfigAutoConfiguration我们看到这里面其实注入了一个 Nacos 配置刷新的源码关键 NacosContextRefresherBean
@Configuration@ConditionalOnProperty(name?=?"spring.cloud.nacos.config.enabled",?matchIfMissing?=?true)public?class?NacosConfigAutoConfiguration?{ //?Nacos?配置属性@Beanpublic?NacosConfigProperties?nacosConfigProperties(ApplicationContext?context)?{ if?(context.getParent()?!=?null&&?BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context.getParent(),?NacosConfigProperties.class).length?>?0)?{ return?BeanFactoryUtils.beanOfTypeIncludingAncestors(context.getParent(),NacosConfigProperties.class);}return?new?NacosConfigProperties();}//?Nacos?配置刷新属性@Beanpublic?NacosRefreshProperties?nacosRefreshProperties()?{ return?new?NacosRefreshProperties();}//?Nacos?刷新历史@Beanpublic?NacosRefreshHistory?nacosRefreshHistory()?{ return?new?NacosRefreshHistory();}//?Nacos?配置管理@Beanpublic?NacosConfigManager?nacosConfigManager(NacosConfigProperties?nacosConfigProperties)?{ return?new?NacosConfigManager(nacosConfigProperties);}//?Nacos?配置刷新@Beanpublic?NacosContextRefresher?nacosContextRefresher(NacosConfigManager?nacosConfigManager,NacosRefreshHistory?nacosRefreshHistory)?{ return?new?NacosContextRefresher(nacosConfigManager,?nacosRefreshHistory);}}NacosContextRefresher 配置中心刷新public?NacosContextRefresher(NacosConfigManager?nacosConfigManager,NacosRefreshHistory?refreshHistory)?{ //?获取配置属性信息this.nacosConfigProperties?=?nacosConfigManager.getNacosConfigProperties();//?刷新历史this.nacosRefreshHistory?=?refreshHistory;//?获取配置服务this.configService?=?nacosConfigManager.getConfigService();//?是否开启刷新,是源码truethis.isRefreshEnabled?=?this.nacosConfigProperties.isRefreshEnabled();}获取配置服务 getConfigServicenacosConfigManager.getConfigService(),这行代码其实就是源码为了创建 NcaosConfigService 对象,我们看看你是源码主力资金源码指标 无未来怎么创建的,其实核心代码就是源码通过 NacosFactory 反射创建的 NcaosConfigService 对象,这个对象是源码一个核心对象后续会讲到的
public?static?ConfigService?createConfigService(Properties?properties)?throws?NacosException?{ try?{ //?加载?NacosConfigService?类Class<?>?driverImplClass?=?Class.forName("com.alibaba.nacos.client.config.NacosConfigService");//?获取构造器Constructor?constructor?=?driverImplClass.getConstructor(Properties.class);//?创建实例ConfigService?vendorImpl?=?(ConfigService)?constructor.newInstance(properties);return?vendorImpl;}?catch?(Throwable?e)?{ throw?new?NacosException(NacosException.CLIENT_INVALID_PARAM,?e);}}监听器NacosContextRefresher 实现了 ApplicationListener ,一看这就是源码一个监听器了,我们看看这个在监听器里面做了什么操作
@Overridepublic?源码void?onApplicationEvent(ApplicationReadyEvent?event)?{ //?这是一个?CAS?操作,只设置一次if?源码(this.ready.compareAndSet(false,?true))?{ //?注册?Nacos?监听器对于应用this.registerNacosListenersForApplications();}}注册 Nacos 监听/**register Nacos Listeners. 注册Nacos监听器 */ private void registerNacosListenersForApplications() { // 默认是 true if (isRefreshEnabled()) { // 遍历Nacos属性资源中心 for (NacosPropertySource propertySource : NacosPropertySourceRepository .getAll()) { if (!propertySource.isRefreshable()) { continue; } // 获取资源ID ?String dataId = propertySource.getDataId(); // 通过组和 dataId 注册 Nacos 监听器 registerNacosListener(propertySource.getGroup(), dataId); } } }
private void registerNacosListener(final String groupKey, final String dataKey) { // 构建 Key 信息 String key = NacosPropertySourceRepository.getMapKey(dataKey, groupKey); // 在 listenerMap中放入了 key 对应 AbstractSharedListener 响应的方法 Listener listener = listenerMap.computeIfAbsent(key, lst -> new AbstractSharedListener() { @Override public void innerReceive(String dataId, String group, String configInfo) { // 刷新次数 refreshCountIncrement(); // 记录刷新历史,就是源码改变历史 nacosRefreshHistory.addRefreshRecord(dataId, group, configInfo); // 发布刷新事件 applicationContext.publishEvent( new RefreshEvent(this, null, "Refresh Nacos config")); } }); // 向配置服务中添加监听器 configService.addListener(dataKey, groupKey, listener);
}
####?向配置服务中添加监听器 此时调用的是?NacosConfigService?中的?addListener?方法,但是源码最终执行的还是?ClientWorker?中的addTenantListeners?方法,后面会进行分析?源码ClientWorker?这个类的```java@Overridepublic?void?addListener(String?dataId,?String?group,?Listener?listener)?throws?NacosException?{ //?这个?ClientWorker?worker?也是一个核心类worker.addTenantListeners(dataId,?group,?Arrays.asList(listener));}构建 CacheData 信息此时向 ClientWorker 中的 CacheData 中添加数据,之后遍历监听器添加到 CacheData 中
创建 CacheData 对象public?源码CacheData(ConfigFilterChainManager?configFilterChainManager,?String?name,?String?dataId,?String?group,String?tenant)?{ //?dataId?不能为空if?(null?==?dataId?||?null?==?group)?{ throw?new?IllegalArgumentException("dataId="?+?dataId?+?",?group="?+?group);}this.name?=?name;this.configFilterChainManager?=?configFilterChainManager;this.dataId?=?dataId;?//?设置dataIdthis.group?=?group;?//?设置组信息this.tenant?=?tenant;?//?设置租户listeners?=?new?CopyOnWriteArrayList<ManagerListenerWrap>();?//?装饰器集合this.isInitializing?=?true;//?加载缓存数据从本地磁盘this.content?=?loadCacheContentFromDiskLocal(name,?dataId,?group,?tenant);//?计算本地缓存信息的MD5this.md5?=?getMd5String(content);}向 CacheData 中添加数据public?void?addTenantListeners(String?dataId,?String?group,List<extends?Listener>?listeners)throws?NacosException?{ //?DefaultGroupgroup?=?null2defaultGroup(group);String?tenant?=?agent.getTenant();?//?是?""//?向缓存数据中添加监听器CacheData?cache?=?addCacheDataIfAbsent(dataId,?group,?tenant);for?(Listener?listener?:?listeners)?{ cache.addListener(listener);}}public?CacheData?addCacheDataIfAbsent(String?dataId,?String?group,?String?tenant)throws?NacosException?{ //?获取Key信息String?key?=?GroupKey.getKeyTenant(dataId,?group,?tenant);//?在缓存?Map?中获取缓存数据CacheData?cacheData?=?cacheMap.get(key);//?如果不为空的情况下那么就返回,如果为空那么就创建一个?CacheDataif?(cacheData?!=?null)?{ return?cacheData;}//?创建一个?CacheData?cacheData?=?new?CacheData(configFilterChainManager,?agent.getName(),?dataId,?group,?tenant);//?将创建好的?cacheData?放入缓存?Map?中CacheData?lastCacheData?=?cacheMap.putIfAbsent(key,?cacheData);//?如果缓存数据为空的话那么从配置中心拉取,不过此时不为空if?(lastCacheData?==?null)?{ //fix?issue?#?if?(enableRemoteSyncConfig)?{ String[]?ct?=?getServerConfig(dataId,?group,?tenant,?L);cacheData.setContent(ct[0]);}//?计算任务IDint?taskId?=?cacheMap.size()?/?(int)?ParamUtil.getPerTaskConfigSize();//?设置任务IDcacheData.setTaskId(taskId);lastCacheData?=?cacheData;}//?缓存数据初始化完成//?reset?so?that?server?not?hang?this?checklastCacheData.setInitializing(true);LOGGER.info("[{ }]?[subscribe]?{ }",?agent.getName(),?key);MetricsMonitor.getListenConfigCountMonitor().set(cacheMap.size());//?返回最新的缓存数据return?lastCacheData;}到这里 CacheData 对象 和 cacheMap 集合已经构建完成了,后续会用到这个数据的
NacosConfigService 分析NacosConfigService这个类在创建的时候主要做了什么事情,这这里面创建了一个 ClientWorker对象,这个对象是一个核心的类,有关于配置的一些操作都是归功于 ClientWorker类
public?NacosConfigService(Properties?properties)?throws?NacosException?{ ?......?this.agent?=?new?MetricsHttpAgent(new?ServerHttpAgent(properties));?this.agent.start();?//?核心工作类?this.worker?=?new?ClientWorker(this.agent,this.configFilterChainManager,?properties);}核心配置类 ClientWorker分析一下这个类都在做什么事情,都有哪些核心方法 其实能看到里面有一个构造函数、添加缓存数据、添加监听器、检查配置中心相关方法、获取服务配置、解析数据响应、gpcopy源码移除缓存数据、删除监听器以及 shutdown方法
构造函数看到这里其实看到了定义了两个调度线程池,一个是用于配置检测的,一个是用于执行长轮询服务的
@SuppressWarnings("PMD.ThreadPoolCreationRule")public?ClientWorker(final?HttpAgent?agent,?final?ConfigFilterChainManager?configFilterChainManager,?final?Properties?properties){ this.agent?=?agent;this.configFilterChainManager?=?configFilterChainManager;//?初始化操作init(properties);//?定义一个调度线程池,只有一个线程还是守护线程this.executor?=?Executors.newScheduledThreadPool(1,?new?ThreadFactory()?{ @Overridepublic?Thread?newThread(Runnable?r)?{ Thread?t?=?new?Thread(r);t.setName("com.alibaba.nacos.client.Worker."?+?agent.getName());t.setDaemon(true);return?t;}});//?定义一个多个线程的调度线程池,线程个数和CPU?核心数有关,也是守护线程,是一个长轮询this.executorService?=?Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors(),?new?ThreadFactory()?{ @Overridepublic?Thread?newThread(Runnable?r)?{ Thread?t?=?new?Thread(r);t.setName("com.alibaba.nacos.client.Worker.longPolling."?+?agent.getName());t.setDaemon(true);return?t;}});//?定义一个定时的调度任务,第一次执行的时候延时1毫秒,后续毫秒调度一次this.executor.scheduleWithFixedDelay(new?Runnable()?{ @Overridepublic?void?run()?{ try?{ //?检查配置信息方法checkConfigInfo();}?catch?(Throwable?e)?{ LOGGER.error("["?+?agent.getName()?+?"]?"+?"[sub-check]?rotate?check?error",?e);}}},?1L,?L,?TimeUnit.MILLISECONDS);}检查配置服务方法这个 cacheMap 包含了一些任务信息,这里面的任务是怎么来的呢,他是在添加监听器的时候添加的,上面已经分析过了
public?NacosContextRefresher(NacosConfigManager?nacosConfigManager,NacosRefreshHistory?refreshHistory)?{ //?获取配置属性信息this.nacosConfigProperties?=?nacosConfigManager.getNacosConfigProperties();//?刷新历史this.nacosRefreshHistory?=?refreshHistory;//?获取配置服务this.configService?=?nacosConfigManager.getConfigService();//?是否开启刷新,是truethis.isRefreshEnabled?=?this.nacosConfigProperties.isRefreshEnabled();}0长轮询任务 LongPollingRunnableSpring 中无处不在的 Properties
Spring中的Properties配置对于不熟悉它的人来说可能会感到复杂,因为它提供了多种配置方式和使用途径。本文并非深度解析源码,而是旨在帮助读者更好地理解和运用Spring的Properties功能。Properties的使用
所有Spring用户都曾接触过Properties,以下是一些常见的使用场景:在XML配置文件中,Properties会自动替换${ }中的值。
通过@Autowired注解注入,如:`@Value("${ javadoop.jdbc.url}") private String url;`
通过Environment接口获取,但在使用时需注意,只有使用了@PropertySource的配置才能通过Environment获取,例如:`@Autowired private Environment env; public String getUrl() { return env.getProperty("javadoop.jdbc.url"); }`
在Spring Boot的prefix源码application.properties中注册的配置同样有效。
Properties的配置
Spring提供了多种配置Properties的方式:最常见的XML配置,如配置文件中的内容。
使用@PropertySource消除XML,如:`@PropertySource("classpath:sys.properties") @Configuration public class JavaDoopConfig { }`
PropertyPlaceholderConfigurer和PropertySourcesPlaceholderConfigurer在Spring 3.1之后提供了新的替代方案。
Spring Boot相关
Spring Boot简化了配置过程。例如:使用start.spring.io快速创建项目,应用默认的application.properties文件。
环境相关的配置可以通过application-{ env}.properties来区分,如:`java -Dspring.profiles.active=prd -jar app.jar`
Spring Boot的@ConfigurationProperties注解允许自动从配置文件中注入Bean属性。
动态修改属性
启动时可以动态设置属性值,覆盖顺序为:启动参数 > application-{ env}.properties > application.properties。总结
要深入了解Spring的Properties,需要研究Environment接口。对于有一定经验的Java工程师,可以通过加入相关技术群组获取更多学习资源,如Java架构学习资料,提升自己的技术水平。Nacos系列如何通过Nacos拉取和自动刷新Spring Boot配置?
大家好,本文将向您展示如何在Spring Boot应用中利用Nacos进行配置的拉取和自动刷新。
首先,确保在您的项目中添加Nacos的依赖,如0.2.版本,这适用于Spring Boot 2.x。对于Spring Boot 1.x版本,可能需要使用0.1.x.RELEASE版本。profiler源码
在application.yml中配置Nacos服务器的地址,以连接到您的Nacos服务。
在项目入口类中,通过@NacosPropertySource引入db.properties配置源,并开启自动更新功能,以实时获取Nacos中的配置。
使用@NacosValue注解在ConfigController中设置属性值,并启用自动刷新,以便在配置更改时自动更新。
值得注意的是,如果系统中已存在username变量,它的值将优先于Nacos中的配置。
为了处理从Nacos获取的配置,实现NacosConfigConverter接口,将Nacos配置映射到自定义的DatabaseInfo实体类。
通过@NacosConfigListener注解,定义了两个监听方法:一个在配置变更时自动转换为DatabaseInfo对象,另一个用于打印变更后的配置信息。
在测试阶段,先在Nacos控制台配置相关参数,启动项目后,当Nacos中的password属性值改变,IDEA控制台将显示更新后的信息,验证配置自动刷新功能正常工作。
本文的studygolang源码完整示例源码可以参考以下链接:
springboot读取配置(springboot读取配置文件)
springboot获取复杂的list配置文件
1、我们在使用@ConfigurationProperties(prefix=system)读取yml配置文件过程中会遇到读取yml文件中列表。如:yml文件设置访问白名单,Config里面使用List集合接收。方法比较简单。
2、配置文件SpringBoot使用一个全局的配置文件application.propertiesapplication.yml配置文件的作用:修改SpringBoot自动配置的默认值,SpringBoot在底层都给我们自动配置好。
3、第@Configuration注解,读取配置到类中,批量注入配置属性第Environment对象,获取配置文件中所有的属性的对象如果你想掌握时下热门微服务技术栈,跟上时代技术步伐,就去黑马程序员官网视频库看免费视频。
4、nacos跟apollo的处理思路有点不一样,找了很多资料,貌似没有找到nacos里直接获取整个获取配置文件的方法,后面如果有同学找到了这个方法记得留言提醒我。nacos在springboot启动的时候已经把所有配置文件都注入到了spring里。
Springboot打成JAR包后读取外部配置文件1、自定义jar目前只能读取application.properties和application.yml吧。可以用一个简单点的方法进行监听注入,在工具包内写一个init方法,在springboot启动时初始化就可以了。比如springboot启动完成后读取配置文件,然后注入到工具包内。
2、在此处,我们可以使用java-jardemo-0.-SNAPSHOT来运行项目。正常运行。当我们将其打成jar包时,application.properties同样不会被打包进jar包中。需要另外复制出来和jar包放在才能正常运行。
3、以jar包发布springboot项目时,默认会先使用jar包同级目录下的application.properties来作为项目配置文件。但使用--spring.config.location指定了配置文件,则读取指定的配置文件。
SpringBoot配置文件存放位置以及读取顺序SpringBoot系统启动时默认会读取的配置文件,支持properties\yml格式。也就是说,会先加载application.properties,根据spring.profile.active的设置加载相应的application-XX.properties配置,然后按优先级合并配置文件。
使用springboot的方式,此种方式jar需要放到前面格式例如以jar包发布springboot项目时,默认会先使用jar包同级目录下的application.properties来作为项目配置文件。
application.properties中:server.port=,application.yml中:server.port=。问题:springboot是否都加载这两个配置文件?如果两个文件有相同的key,取哪一个文件的value?答:都加载,且按properties→yml的顺序加载。
springboot配置文件支持灵活的路径,以及灵活的文件名,用一个变量表达式总结如下:部分源码如下:当满足上述变量表达式的配置文件有多个时,会有一个配置的优先级。
springboot中获取apollo或者nacos里的配置文件
此时,需要在bootstrap.yml中添加springcloud配置:(至于为什么是bootstrap.yml而不是application.yml,这又是另一个问题了)有了上面的配置,程序启动后,就能正常的从nacos配置中心获取配置了。
在application.yaml配置文件中指定nacos中配置的DataID不会生效,需要通过注解@NacosPropertySource指定才能生效。
nacos-config这个依赖就相当于SpringCloudConfig,nacos-discovery这个依赖就相当于Eureka。
一文讲清楚SpringBoot六种读取配置方式1、使用开发工具创建一个纯净maven工程删除配置spring-biz.xml并且新增如下配置:本文梳理了SpringBoot六种读取配置方式,大家在项目中可以进行灵活组合和应用,希望本文对大家有所帮助。
2、则只会根据classloader的classpath列表,选取第一个出现的文件。因为springboot加载配置文件时最底层是使用的下面的方法:这两个方法只会获取classloader类的ucp属性里面第一个匹配到的值。
3、idea中,为了我们本地方便开发测试,我们在此处创建一个config目录,然后把application.properties放进去,项目正常运行。jar包会自动生成在target目录下。
4、配置文件SpringBoot使用一个全局的配置文件application.propertiesapplication.yml配置文件的作用:修改SpringBoot自动配置的默认值,SpringBoot在底层都给我们自动配置好。
5、SpringBoot中的配置文件主要有三种格式,properties、yaml、和xml方式。
6、SpringBoot系统启动时默认会读取的配置文件,支持properties\yml格式。也就是说,会先加载application.properties,根据spring.profile.active的设置加载相应的application-XX.properties配置,然后按优先级合并配置文件。
springboot配置文件读取nacos在springboot启动的时候已经把所有配置文件都注入到了spring里。
idea中,为了我们本地方便开发测试,我们在此处创建一个config目录,然后把application.properties放进去,项目正常运行。jar包会自动生成在target目录下。
这个问题的存在是因为在spring跑到@serviceclass的时候,我们还没有取到配置文件里面的value,所以导致了spring认为这个valuevariable没有initialize。
关于如果修改默认的加载路径和文件名,后面会继续讨论。SpringBoot系统启动时默认会读取的配置文件,支持properties\yml格式。
SpringBoot全局配置文件加载原理详解(万字源码分析)
SpringBoot通过全局配置文件实现项目部署和修改的便利性,以application.properties或application.yaml为核心,配置文件加载遵循特定的优先级规则:从classpath:/config/开始,依次是file:./config/、classpath:/、file:./,且越靠前的路径优先级越高。
配置文件的生效依赖于ApplicationListener实现类,如ConfigFileApplicationListener,它监听ApplicationEnvironmentPreparedEvent事件。当项目启动时,会从spring.factories文件中加载并实例化这些监听器,如ConfigFileApplicationListener负责加载配置文件。
启动过程中的关键步骤包括:首先,从spring.factories中获取监听器,如EventPublishingRunListener,然后通过事件广播机制触发加载配置文件的步骤。当遇到ApplicationEnvironmentPreparedEvent时,ConfigFileApplicationListener的load方法开始检索配置文件,优先级高的配置文件会覆盖低的。
加载过程涉及PropertySourceLoader,如PropertiesPropertySourceLoader和YamlPropertySourceLoader,它们根据文件扩展名(properties或yaml)检索并加载配置。具体操作包括读取配置文件、解析键值对,然后以Document对象的形式返回给上层处理。
总结来说,SpringBoot的全局配置文件加载是一个从配置文件路径查找、内容读取、解析到加载的完整流程,确保项目的配置能够在启动时正确生效。