@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


    @RefreshScope那些事_第1张图片
    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 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);
            }
        }
      

    你可能感兴趣的:(@RefreshScope那些事)