SpringCloud的动态配置实现原理
前言
动态配置(变动频率高的,如支付方式,页面展示信息)和静态配置(数据库连接配置等)
- 核心注解@RefreshScope
对Bean创建动态代理 - 核心类RefreshScope
负责销毁被@RefreshScope注解配置的Bean - ContextRefresher
负责刷新环境Environment,更新Environment中的所有配置值
Spring Cloud负责更新环境Environment以及创建新的动态配置bean,而判断配置是否改变,以及怎么获取新的配置则是由第三方框架实现的,下面的测试都以 Nacos 为例。
第一种方式
使用@ConfigurationProperties 注解,不需要使用 @RefreshScope注解
复制myuser:
name: lisi
复制import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Data
@Component
@ConfigurationProperties(prefix = "myuser")
public class UserProperties {
private String name;
}
复制@RestController
@RequestMapping("/test")
public class TestController {
@Autowired
private UserProperties userProperties;
@Autowired
private ContextRefresher contextRefresher;
@GetMapping("/testRefreshExecute")
@ApiOperation("测试刷新对象执行")
public void testRefreshExceute() {
//这个步骤一般由配置中心框架来触发
contextRefresher.refresh();
}
@GetMapping("/testRefreshQuery")
@ApiOperation("测试刷新对象查询")
public String testRefreshQuery() {
return userProperties.getName();
}
}
测试步骤
- /test/testRefreshQuery获取旧的配置值
- 修改配置中心的值
- Nacos 会通过长连接监听到此修改,调用ContextRefresher的 refresh() 方法,会发送EnvironmentChangeEvent事件,ConfigurationPropertiesRebinder监听此事件并重新绑定属性值(ConfigurationPropertiesBindingPostProcessor)
- /test/testRefreshQuery获取新的配置值
这种方式的原理就是重新走Bean的初始化流程。
第二种方式
使用 @RefreshScope 注解
复制import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;
@Data
@Component
@RefreshScope
public class UserProperties {
@Value("${myuser.name}")
private String name;
}
复制@RestController
@RequestMapping("/test")
public class TestController {
@Autowired
private UserProperties userProperties;
@Autowired
private ContextRefresher contextRefresher;
@GetMapping("/testRefreshExecute")
@ApiOperation("测试刷新对象执行")
public void testRefreshExceute() {
//这个步骤一般由配置中心框架来触发
contextRefresher.refresh();
}
@GetMapping("/testRefreshQuery")
@ApiOperation("测试刷新对象查询")
public String testRefreshQuery() {
return userProperties.getName();
}
}
- /test/testRefreshQuery获取旧的配置值
- 修改配置中心的值
- Nacos 会通过长连接监听到此修改,调用ContextRefresher的 refresh() 方法,发送EnvironmentChangeEvent事件,并调用RefreshScope的refreshAll()方法清空Bean缓存。
- /test/testRefreshQuery获取新的配置值
@RefreshScope注解会创建动态代理对象,实际类型为ScopedProxyFactoryBean,每次调用方法都会调用SimpleBeanTargetSource的getTarget()方法,RefreshScope的get(beanName, objectFactory)第一次从IOC容器创建,后续走缓存。
复制@Override
public Object get(String name, ObjectFactory<?> objectFactory) {
BeanLifecycleWrapper value = this.cache.put(name,
new BeanLifecycleWrapper(name, objectFactory));
this.locks.putIfAbsent(name, new ReentrantReadWriteLock());
try {
return value.getBean();
}
catch (RuntimeException e) {
this.errors.put(name, e);
throw e;
}
}
@Override
public void destroy() {
List<Throwable> errors = new ArrayList<Throwable>();
Collection<BeanLifecycleWrapper> wrappers = this.cache.clear();
for (BeanLifecycleWrapper wrapper : wrappers) {
try {
Lock lock = this.locks.get(wrapper.getName()).writeLock();
lock.lock();
try {
wrapper.destroy();
}
finally {
lock.unlock();
}
}
}
}
this.cache.put()方法底层是putIfAbsent(),所以多次使用的是同一个BeanLifecycleWrapper对象。destroy()方法会清空cache,下次使用的就是新创建的BeanLifecycleWrapper了(重新创建 Bean)。
总结
- NacosContextRefresher 类的 registerNacosListener() 方法会注册一个监听器,发送 RefreshEvent 事件,RefreshEventListener 来处理此事件,内部就是调用 ContextRefresher 的 refresh() 方法。
- 上面两种方式的底层原理都是会重新走 Bean 的初始化过程,在这个过程中就会重新获取配置值。
参考
Spring Cloud动态配置实现原理与源码分析
Nacos共享配置(shared-configs)和扩展配(extension-config)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix