Hello,我是大都督周瑜,本文给大家分析一下@ConfigurationProperties
结合Nacos配置动态刷新的底层原理,记得点赞、关注、分享哦!
公众号:IT周瑜
应用背景
假如在Nacos中有Data ID为common.yml
的配置项:
model:name: gpt-4
在应用的application.yml
中进行导入:
spring:config:import: optional:nacos:common.yml
对应Properties类为ModelProperties
:
@Data
@Component
@ConfigurationProperties(prefix = "model")
public class ModelProperties {private String name;
}
在ZhouyuService
中使用ModelProperties:
@Service
public class ZhouyuService {@Resourceprivate ModelProperties modelProperties;public String test() {return modelProperties.getName();}
}
直接在Nacos中进行配置修改,ZhouyuService
都能及时获取到最新的配置,注意这里的用法为modelProperties.getName()
。
原理分析
先在ZhouyuService
中进行Debug,查看配置更新前后ModelProperties对象是否是同一个对象。
配置更新前:
配置更新后:
对象是同一个,但属性值发生了变化,所以底层原理应该是:Nacos客户端监听到配置发生了变化之后,会找到ModelProperties对象,然后调用name的set方法进行属性值的更新。
启动过程的初始化
首先,在Spring Cloud中,定义了一个ConfigurationPropertiesBeans
Bean对象,它有两个功能:
- 首先,它是一个
BeanPostProcessor
- 其次,它里面有一个
Map<String, ConfigurationPropertiesBean>
类型的beans
属性
作为BeanPostProcessor
,在它的初始化前方法中,会对Spring容器中的每个Bean对象进行判断,会过滤出那些加了@ConfigurationProperties
注解的Bean,在本文中,指的就是ModelProperties
对象,找到ModelProperties
对象后,会把它包装为一个ConfigurationPropertiesBean
对象,并存在beans
这个Map中,后续配置发生变化时,会从beans
中取出ModelProperties
对象并进行属性值的更新。
另外,Nacos的自动配置类NacosConfigAutoConfiguration
中提供了一个NacosContextRefresher
的Bean对象,它是一个ApplicationListener
,它监听了ApplicationReadyEvent
事件:
在Spring Boot启动过程的最后,Spring Boot会发布ApplicationReadyEvent
事件,从而触发NacosContextRefresher
的事件处理逻辑,NacosContextRefresher
接收到ApplicationReadyEvent
事件后,会向Naocs客户端的ConfigService
中注册一个Nacos配置监听器,用来监听Naocs服务端配置的改变。
因此在Spring Boot启动过程中,核心会做两件事:
- 找到加了
@ConfigurationProperties
注解的Bean,并存下来 - 注册一个Nacos的配置监听器
配置发生变化时
一旦Nacos服务端的配置发生了变化,就会触发执行Nacos客户端的配置监听器,配置监听器会利用Spring容器发布一个RefreshEvent
事件,该事件是Spring Cloud定义的。
Spring Cloud中定义了一个RefreshEventListener
,就是用来处理RefreshEvent
事件的:
而它的核心逻辑是更新Spring容器的Environment
对象:
我们可以把Environment
对象理解为Nacos服务端的配置项在客户端的本地缓存,因此Nacos客户端一旦发现服务端配置发生了改变,就会发布RefreshEvent
事件,从而将Environment
对象中的缓存的配置项更新为新值。
同时,在更新完Environment
对象后,会再次利用Spring容器发布一个EnvironmentChangeEvent
事件。
在Spring Cloud中,还定义了一个ConfigurationPropertiesRebinder
Bean对象:
它会处理EnvironmentChangeEvent
事件,它会用到前面提到的ConfigurationPropertiesBeans
对象,遍历它的Map中所存的那些加了@ConfigurationProperties
注解的Bean:
比如ModelProperties
对象,并针对每个Bean进行rebind()
操作。
所谓rebind()
操作,其实就是先从容器中获取到指定的Bean对象,也就是加了@ConfigurationProperties
注解的Bean对象,先进行Bean销毁,再进行Bean初始化:
而Bean的初始化过程中,会执行到ConfigurationPropertiesBindingPostProcessor
中的初始化前方法,会对ModelProperties
对象重新进行bind:
而bind的过程,就是利用Environment
对象中的值更新ModelProperties
对象中的属性,从而完成配置的刷新,这块细节暂时就不分析了。
总结
当Nacos服务端的配置发生改变时,会触发Nacos客户端的配置监听器,从而发布RefreshEvent
事件,从而更新Environment
对象,从而发布EnvironmentChangeEvent
事件,从而利用最新的Environment
对象更新ModelProperties
对象中的属性。