Spring Cloud Config 动态刷新原理分析
Spring Cloud Config的数据刷新机制
底层工作原理依赖于Sping Actuator组件,在数据源发生变化的时候调用 /actuator/refresh 来应用实例完成config配置的的刷新
可以刷新的配置主要有两种形式一种是
1、@ConfigurationProperties
声明的配置类
2、@RefreshScope
声明的bean
刷新流程源码分析
- 调用
/actuator/refresh
端点 - 刷新环境属性(
ContextRefresher
) - 发布
EnvironmentChangeEvent
事件 - 动态更新 Bean
接下来我们将结合源码来分析每个步骤。
1. 调用 /actuator/refresh
端点
当你调用 /actuator/refresh
端点时,Spring Actuator 的 RefreshEndpoint
类会处理这个请求。
RefreshEndpoint
类(暴露 /refresh
端点):
/**
* @author Dave Syer
* @author Venil Noronha
*/
@ConfigurationProperties(prefix = "endpoints.refresh", ignoreUnknownFields = false)
@ManagedResource
public class RefreshEndpoint extends AbstractEndpoint<Collection<String>> {
private ContextRefresher contextRefresher;
public RefreshEndpoint(ContextRefresher contextRefresher) {
super("refresh");
this.contextRefresher = contextRefresher;
}
@ManagedOperation
public String[] refresh() {
Set<String> keys = contextRefresher.refresh();
return keys.toArray(new String[keys.size()]);
}
@Override
public Collection<String> invoke() {
return Arrays.asList(refresh());
}
}
contextRefresher.refresh()
:实际的配置刷新逻辑由ContextRefresher
完成。
当 /refresh
端点被调用时,RefreshEndpoint
会触发 ContextRefresher.refresh()
方法。
2. 刷新环境属性(ContextRefresher
)
ContextRefresher
类是配置刷新流程的核心,它负责重新加载环境中的配置并触发配置变更事件。关键方法是 refresh()
,如下所示:
ContextRefresher
类:
public synchronized Set<String> refresh() {
Map<String, Object> before = extract(
this.context.getEnvironment().getPropertySources());
addConfigFilesToEnvironment();
Set<String> keys = changes(before,
extract(this.context.getEnvironment().getPropertySources())).keySet();
this.context.publishEvent(new EnvironmentChangeEvent(context, keys));
this.scope.refreshAll();
return keys;
}
environmentManager.reload()
:通过EnvironmentManager
从远程配置中心重新加载配置。它会将新的配置值注入到 Spring 的Environment
中,并返回那些发生变化的配置键。publishEvent(new EnvironmentChangeEvent(...))
:如果配置有变化,ContextRefresher
会发布EnvironmentChangeEvent
事件,将变化的配置键传递给应用上下文。context.refresh()
:有些情况下可能会调用ApplicationContext.refresh()
方法以刷新整个上下文(这不是必须的,具体依赖应用场景)。
ContextRefresher
是整个配置刷新的核心,它通过 EnvironmentManager
从远程配置中心重新加载配置,并在有变化时发布 EnvironmentChangeEvent
事件。
3. 发布 EnvironmentChangeEvent
事件
EnvironmentChangeEvent
是 Spring 框架中的一个事件,用来通知应用中的相关组件,某些环境属性已经发生了变化。
/**
* Event published to signal a change in the {@link Environment}.
*
* @author Dave Syer
*
*/
@SuppressWarnings("serial")
public class EnvironmentChangeEvent extends ApplicationEvent {
private Set<String> keys;
public EnvironmentChangeEvent(Set<String> keys) {
// Backwards compatible constructor with less utility (practically no use at all)
this(keys, keys);
}
public EnvironmentChangeEvent(Object context, Set<String> keys) {
super(context);
this.keys = keys;
}
/**
* @return the keys
*/
public Set<String> getKeys() {
return keys;
}
}
EnvironmentChangeEvent
类:
ApplicationEvent
:EnvironmentChangeEvent
继承了 Spring 的ApplicationEvent
,是一种标准的事件模型。它封装了当前的ApplicationContext
和那些已经发生变化的配置键。getKeys()
:提供方法来获取所有发生变化的配置键。
事件发布之后,Spring 容器中那些监听 EnvironmentChangeEvent
事件的组件会相应地做出反应。通常,使用 @ConfigurationProperties
或 @RefreshScope
的 Bean 会监听这个事件,并更新其绑定的属性。
4. 动态更新 Bean
当 EnvironmentChangeEvent
被发布时ConfigurationPropertiesRebinder
接收到通知,会对Bean进行销毁并且重新完成初始化以此来达到刷新配置属性的目的
具体流程:
@ManagedOperation
public boolean rebind(String name) {
if (!this.beans.getBeanNames().contains(name)) {
return false;
}
if (this.applicationContext != null) {
try {
Object bean = this.applicationContext.getBean(name);
if (AopUtils.isAopProxy(bean)) {
bean = ProxyUtils.getTargetObject(bean);
}
this.applicationContext.getAutowireCapableBeanFactory().destroyBean(bean);
this.applicationContext.getAutowireCapableBeanFactory()
.initializeBean(bean, name);
return true;
}
catch (RuntimeException e) {
this.errors.put(name, e);
throw e;
}
}
return false;
}