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源码中看到有哪个方法发布过

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