@RefreshScope源码解读

@RefreshScope那些事
要说清楚RefreshScope,先要了解Scope
Scope(org.springframework.beans.factory.config.Scope)是Spring 2.0开始就有的核心的概念

RefreshScope(org.springframework.cloud.context.scope.refresh)是spring cloud提供的一种特殊的scope实现,用来实现配置、实例热加载。

Scope -> GenericScope -> RefreshScope

scope_hierarchy.jpeg
Scope与ApplicationContext生命周期

AbstractBeanFactory#doGetBean创建Bean实例
protected T doGetBean(…){
final RootBeanDefinition mbd = …
if (mbd.isSingleton()) {

} else if (mbd.isPrototype())

} else {
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
Object scopedInstance = scope.get(beanName, new ObjectFactory() {…});

}

}
Singleton和Prototype是硬编码的,并不是Scope子类。 Scope实际上是自定义扩展的接口
Scope Bean实例交由Scope自己创建,例如SessionScope是从Session中获取实例的,ThreadScope是从ThreadLocal中获取的,而RefreshScope是在内建缓存中获取的。
@Scope 对象的实例化

@RefreshScope 是scopeName="refresh"的 @Scope

@Scope(“refresh”)
public @interface RefreshScope {

}
@Scope 的注册 AnnotatedBeanDefinitionReader#registerBean
public void registerBean(…){

ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
abd.setScope(scopeMetadata.getScopeName());

definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
}
读取@Scope元数据, AnnotationScopeMetadataResolver#resolveScopeMetadata
public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {
AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(
annDef.getMetadata(), Scope.class);
if (attributes != null) {
metadata.setScopeName(attributes.getString(“value”));
ScopedProxyMode proxyMode = attributes.getEnum(“proxyMode”);
if (proxyMode == null || proxyMode == ScopedProxyMode.DEFAULT) {
proxyMode = this.defaultProxyMode;
}
metadata.setScopedProxyMode(proxyMode);
}
}
Scope实例对象通过ScopedProxyFactoryBean创建,其中通过AOP使其实现ScopedObject接口,这里不再展开
现在来说说RefreshScope是如何实现配置和实例刷新的
RefreshScope注册

RefreshAutoConfiguration#RefreshScopeConfiguration
@Component
@ConditionalOnMissingBean(RefreshScope.class)
protected static class RefreshScopeConfiguration implements BeanDefinitionRegistryPostProcessor{

registry.registerBeanDefinition(“refreshScope”,
BeanDefinitionBuilder.genericBeanDefinition(RefreshScope.class)
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE)
.getBeanDefinition());

}
RefreshScope extends GenericScope, 大部分逻辑在 GenericScope 中
GenericScope#postProcessBeanFactory 中向AbstractBeanFactory注册自己
public class GenericScope implements Scope, BeanFactoryPostProcessor…{
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
throws BeansException {
beanFactory.registerScope(this.name/refresh/, this/RefreshScope/);

}
}
RefreshScope 刷新过程

入口在ContextRefresher#refresh
refresh() {
Map<String, Object> before = ①extract(
this.context.getEnvironment().getPropertySources());
②addConfigFilesToEnvironment();
Set keys = ④changes(before,
③extract(this.context.getEnvironment().getPropertySources())).keySet();
this.context.⑤publishEvent(new EnvironmentChangeEvent(keys));
this.scope.⑥refreshAll();
}
①提取标准参数(SYSTEM,JNDI,SERVLET)之外所有参数变量
②把原来的Environment里的参数放到一个新建的Spring Context容器下重新加载,完事之后关闭新容器
③提起更新过的参数(排除标准参数)
④比较出变更项
⑤发布环境变更事件,接收:EnvironmentChangeListener/LoggingRebinder
⑥RefreshScope用新的环境参数重新生成Bean
重新生成的过程很简单,清除refreshscope缓存幷销毁Bean,下次就会重新从BeanFactory获取一个新的实例(该实例使用新的配置)
RefreshScope#refreshAll
public void refreshAll() {
super.destroy();
this.context.publishEvent(new RefreshScopeRefreshedEvent());
}
GenericScope#destroy
public void destroy() {

Collection wrappers = this.cache.clear();
for (BeanLifecycleWrapper wrapper : wrappers) {
wrapper.destroy();
}
}
Spring Cloud Bus 如何触发 Refresh

BusAutoConfiguration#BusRefreshConfiguration 发布一个RefreshBusEndpoint
@Configuration
@ConditionalOnClass({ Endpoint.class, RefreshScope.class })
protected static class BusRefreshConfiguration {

  @Configuration
  @ConditionalOnBean(ContextRefresher.class)
  @ConditionalOnProperty(value = "endpoints.spring.cloud.bus.refresh.enabled", matchIfMissing = true)
  protected static class BusRefreshEndpointConfiguration {
      @Bean
      public RefreshBusEndpoint refreshBusEndpoint(ApplicationContext context,
              BusProperties bus) {
          return new RefreshBusEndpoint(context, bus.getId());
      }
  }

}
RefreshBusEndpoint 会从http端口触发广播RefreshRemoteApplicationEvent事件
@Endpoint(id = “bus-refresh”)
public class RefreshBusEndpoint extends AbstractBusEndpoint {
public void busRefresh() {
publish(new RefreshRemoteApplicationEvent(this, getInstanceId(), null));
}
}
BusAutoConfiguration#refreshListener 负责接收事件(所有配置bus的节点)
@Bean
@ConditionalOnProperty(value = “spring.cloud.bus.refresh.enabled”, matchIfMissing = true)
@ConditionalOnBean(ContextRefresher.class)
public RefreshListener refreshListener(ContextRefresher contextRefresher) {
return new RefreshListener(contextRefresher);
}
RefreshListener#onApplicationEvent 触发 ContextRefresher
public void onApplicationEvent(RefreshRemoteApplicationEvent event) {
Set keys = contextRefresher.refresh();
}
大部分需要更新的服务需要打上@RefreshScope, EurekaClient是如何配置更新的

EurekaClientAutoConfiguration#RefreshableEurekaClientConfiguration
@Configuration
@ConditionalOnRefreshScope
protected static class RefreshableEurekaClientConfiguration{
@Bean
@RefreshScope
public EurekaClient eurekaClient(…) {
return new CloudEurekaClient(manager, config, this.optionalArgs,
this.context);
}

  @Bean
  @RefreshScope
  public ApplicationInfoManager eurekaApplicationInfoManager(...) {
      ...
      return new ApplicationInfoManager(config, instanceInfo);
  }

}

作者:黄大海
链接:https://www.jianshu.com/p/188013dd3d02

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值