Spring之@RefreshScope探祕(二)

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章