SpringCloud之RefreshScope 源码解读

SpringCloud之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
    SpringCloud之RefreshScope 源码解读_第1张图片

  • 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
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Scope("refresh")
@Documented
public @interface RefreshScope {

    /**
     * @see Scope#proxyMode()
     * @return proxy mode
     */
    ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;
}
  • @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
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(this.context, keys));
  return keys;
}
  • ①提取标准参数(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();
    }
}

你可能感兴趣的:(SpringCloud之RefreshScope 源码解读)