ContextRefresher:refresh()的调用
public synchronized Set<String> refresh() {
Set<String> keys = refreshEnvironment();
this.scope.refreshAll();
return keys;
}
public synchronized Set<String> refreshEnvironment() {
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(this.context, keys));
return keys;
}
分析如下:
- 1.提取标准参数(systemProperties,systemEnvironment,configurationProperties,jndiProperties,servletContextInitParams,servletConfigInitParams)之外所有参数变量
- 2.把原来的Environment里的参数放到一个新建的Spring Context容器下重新加载,加载完成之后关闭容器
- 3.提取更新过的参数(排除标准参数)
- 4.比较出变更项
- 5.发布环境变更事件
- 6.通过RefreshScope的refreshAll()方法重新加载所有scope为refresh的bean
触发Refresh事件
1.RefreshEndpoint
@Endpoint(id = "refresh")
public class RefreshEndpoint {
private ContextRefresher contextRefresher;
public RefreshEndpoint(ContextRefresher contextRefresher) {
this.contextRefresher = contextRefresher;
}
@WriteOperation
public Collection<String> refresh() {
Set<String> keys = this.contextRefresher.refresh();
return keys;
}
}
配合spring-boot-actuator使用,只需要发送一个针对refresh路径的POST请求即可,至于与Git结合自动刷新也是这个原理,在修改配置文件提交后通过webhook发送一个POST请求给相关服务器,以达到动态刷新的目的
2.spring-cloud-bus之RefreshListener
public class RefreshListener
implements ApplicationListener<RefreshRemoteApplicationEvent> {
private static Log log = LogFactory.getLog(RefreshListener.class);
private ContextRefresher contextRefresher;
public RefreshListener(ContextRefresher contextRefresher) {
this.contextRefresher = contextRefresher;
}
@Override
public void onApplicationEvent(RefreshRemoteApplicationEvent event) {
Set<String> keys = this.contextRefresher.refresh();
log.info("Received remote refresh request. Keys refreshed " + keys);
}
}
显然,这是一个事件监听器,当ApplicationContext发布了一个RefreshRemoteApplicationEvent 事件时将会触发这个监听器从而达到动态刷新所有scope为refresh的bean
发布RefreshRemoteApplicationEvent
@Endpoint(id = "bus-refresh") // TODO: document new id
public class RefreshBusEndpoint extends AbstractBusEndpoint {
public RefreshBusEndpoint(ApplicationEventPublisher context, String id) {
super(context, id);
}
@WriteOperation
public void busRefreshWithDestination(@Selector String destination) { // TODO:
// document
// destination
publish(new RefreshRemoteApplicationEvent(this, getInstanceId(), destination));
}
@WriteOperation
public void busRefresh() {
publish(new RefreshRemoteApplicationEvent(this, getInstanceId(), null));
}
}
这个跟RefreshEndpoint基本一致,只是多了个带参数的接口
- spring-cloud-bus当然不止这么简单,它可以在分布式系统中管理和传播消息,本质是利用了MQ的广播机制在分布式的系统中传播消息,这个将会在以后的章节中详解
3.RefreshEventListener
public class RefreshEventListener implements SmartApplicationListener {
private static Log log = LogFactory.getLog(RefreshEventListener.class);
private ContextRefresher refresh;
private AtomicBoolean ready = new AtomicBoolean(false);
public RefreshEventListener(ContextRefresher refresh) {
this.refresh = refresh;
}
@Override
public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
return ApplicationReadyEvent.class.isAssignableFrom(eventType)
|| RefreshEvent.class.isAssignableFrom(eventType);
}
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationReadyEvent) {
handle((ApplicationReadyEvent) event);
}
else if (event instanceof RefreshEvent) {
handle((RefreshEvent) event);
}
}
public void handle(ApplicationReadyEvent event) {
this.ready.compareAndSet(false, true);
}
public void handle(RefreshEvent event) {
if (this.ready.get()) { // don't handle events before app is ready
log.debug("Event received " + event.getEventDesc());
Set<String> keys = this.refresh.refresh();
log.info("Refresh keys changed: " + keys);
}
}
}
这是一个事件监听器,当ApplicationContext发布了一个RefreshEvent 事件时将会触发这个监听器从而达到动态刷新所有scope为refresh的bean,不过RefreshEvent 这个事件目前还没在spring源码中看到有哪个方法发布过