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源碼中看到有哪個方法發佈過