SpringCloud之RefreshScope 源碼解讀

SpringCloud之RefreshScope

@Scope 源碼解讀

  • Scope(org.springframework.beans.factory.config.Scope)是Spring 2.0開始就有的核心的概念java

  • RefreshScope(org.springframework.cloud.context.scope.refresh)是spring cloud提供的一種特殊的scope實現,用來實現配置、實例熱加載。spring

  • Scope -> GenericScope -> RefreshScope 緩存

  • Scope與ApplicationContext生命週期app

    • AbstractBeanFactory#doGetBean建立Bean實例
protected <T> 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<Object>() {...});
          ...
    }
    ...
 }
  • 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<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;
}
  • ①提取標準參數(SYSTEM,JNDI,SERVLET)以外全部參數變量
  • ②把原來的Environment裏的參數放到一個新建的Spring Context容器下從新加載,完事以後關閉新容器
  • ③提起更新過的參數(排除標準參數)
  • ④比較出變動項
  • ⑤發佈環境變動事件,接收:EnvironmentChangeListener/LoggingRebinder
  • ⑥RefreshScope用新的環境參數從新生成Bean
  • ⑦從新生成的過程很簡單,清除refreshscope緩存幷銷燬Bean,下次就會從新從BeanFactory獲取一個新的實例(該實例使用新的配置)
  • ⑧RefreshScope#refreshAll
public void refreshAll() {
        <b>super.destroy();</b>
        this.context.publishEvent(new RefreshScopeRefreshedEvent());
}
  • ⑨GenericScope#destroy
public void destroy() {
    ...
    Collection<BeanLifecycleWrapper> wrappers = <b>this.cache.clear()</b>;
    for (BeanLifecycleWrapper wrapper : wrappers) {
        <b>wrapper.destroy();</b>
    }
}
相關文章
相關標籤/搜索