spring cloud的RefreshScope注解进行热部署

需要热加载的bean需要加上@RefreshScope注解,当配置发生变更的时候可以在不重启应用的前提下完成bean中相关属性的刷新。
【spring cloud的RefreshScope注解进行热部署】
经由@RefreshScope修饰的bean将会被RefreshScope代理,其关于bean生命周期的相关方法也在此定义。

@ManagedOperation(description = "Dispose of the current instance of all beans in this scope and force a refresh on next method execution.") public void refreshAll() { super.destroy(); this.context.publishEvent(new RefreshScopeRefreshedEvent()); }

RefreshScope代理的bean强制为懒加载,只有在第一次使用的时候才会生成实例,当其需要刷新配置的时候直接调用destory()方法销毁当前bean,这样在刷新配置后在需要生成的bean已经是根据新的配置信息生成,完成bean的热加载。

在spring cloud中,当配置发生变更,将会通过soring cloud bus发送RefreshRemoteApplicationEvent事件给相关应用,在RefreshListener中,开始对于配置的刷新。
public void onApplicationEvent(RefreshRemoteApplicationEvent event) { Set keys = this.contextRefresher.refresh(); log.info("Received remote refresh request. Keys refreshed " + keys); }

最终刷新将在refreshContext的refresh()方法中进行。
public synchronized Set refresh() { Set keys = refreshEnvironment(); this.scope.refreshAll(); return keys; }public synchronized Set refreshEnvironment() { Map before = extract( this.context.getEnvironment().getPropertySources()); addConfigFilesToEnvironment(); Set keys = changes(before, extract(this.context.getEnvironment().getPropertySources())).keySet(); this.context.publishEvent(new EnvironmentChangeEvent(context, keys)); return keys; }

在这里的刷新一共被分为两个步骤,首先获取新的配置值与老的配置值进行比对并更新,之后再销毁所有被RefreshScope代理的bean。

在第一步,重点在于获取新的配置值,具体实现在addConfigFilesToEnvironment()方法中。
StandardEnvironment environment = copyEnvironment( this.context.getEnvironment()); SpringApplicationBuilder builder = new SpringApplicationBuilder(Empty.class) .bannerMode(Mode.OFF).web(WebApplicationType.NONE) .environment(environment); // Just the listeners that affect the environment (e.g. excluding logging // listener because it has side effects) builder.application() .setListeners(Arrays.asList(new BootstrapApplicationListener(), new ConfigFileApplicationListener())); capture = builder.run(); if (environment.getPropertySources().contains(REFRESH_ARGS_PROPERTY_SOURCE)) { environment.getPropertySources().remove(REFRESH_ARGS_PROPERTY_SOURCE); } MutablePropertySources target = this.context.getEnvironment() .getPropertySources(); String targetName = null; for (PropertySource source : environment.getPropertySources()) { String name = source.getName(); if (target.contains(name)) { targetName = name; } if (!this.standardSources.contains(name)) { if (target.contains(name)) { target.replace(name, source); } else { if (targetName != null) { target.addAfter(targetName, source); } else { // targetName was null so we are at the start of the list target.addFirst(source); targetName = name; } } } }

此处将会以当前的应用的envionment深克隆一份,并根据复制出来的为依据重新建立一个spring boot application,在新的应用建立过程中,将会重新获取配置值,此处也将获取新的配置值,在新的副本应用建立完毕之后,将会返回其配置值准备与旧的配置值进行比对并更新。

    推荐阅读