SpringCloud @RefreshScope实现原理

Posted OkidoGreen

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringCloud @RefreshScope实现原理相关的知识,希望对你有一定的参考价值。

环境:spring cloud context2.2.8.RELEASE + spring boot 2.3.9.RELEASE


1 RefreshScope源码

@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("refresh")注解指明了作用域名为refresh。

2 注册RefreshScope

public class RefreshAutoConfiguration 
  /**
   * Name of the refresh scope name.
   */
  public static final String REFRESH_SCOPE_NAME = "refresh";
  // 注册RefreshScope  
  @Bean
  @ConditionalOnMissingBean(RefreshScope.class)
  public static RefreshScope refreshScope() 
    return new RefreshScope();
  
  // 主要功能是刷新配置文件,发布事件(事件监听程序接收到事件后会重写配置相关的配置类)
  @Bean
  @ConditionalOnMissingBean
  public ContextRefresher contextRefresher(ConfigurableApplicationContext context, RefreshScope scope) 
    return new ContextRefresher(context, scope);
  
  // 监听上下文刷新事件  
  @Bean
  public RefreshEventListener refreshEventListener(ContextRefresher contextRefresher) 
    return new RefreshEventListener(contextRefresher);
    

RefreshScope类

该类的核心方法都在父类中实现

public class RefreshScope extends GenericScope implements ApplicationContextAware, ApplicationListener<ContextRefreshedEvent>,   Ordered 
  public boolean refresh(String name) 
    if (!name.startsWith(SCOPED_TARGET_PREFIX)) 
      name = SCOPED_TARGET_PREFIX + name;
    
    if (super.destroy(name)) 
      this.context.publishEvent(new RefreshScopeRefreshedEvent(name));
      return true;
    
    return false;
  

  @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());
  
    

该类继承自GenericScope,而GenericScope继承BeanFactoryPostProcessor。

public class GenericScope implements Scope, BeanFactoryPostProcessor, BeanDefinitionRegistryPostProcessor, DisposableBean 
  @Override
  public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException 
    this.beanFactory = beanFactory;
    beanFactory.registerScope(this.name, this);
    setSerializationId(beanFactory);
  

在这里会注册RefreshScope。注册的Scope将会在AbstractBeanFactory#doGetBean方法中调用,该方法中会先拿到当前BeanDefinition中定义的Scope,通过scopeName从Map集合中拿到Scope类,最后调用Scope的get方法获取实例对象;详细参见15例,查看get方法调用时机。

添加了@RefreshScope注解的Bean对象会执行RefreshScope#get方法

public class GenericScope implements Scope, BeanFactoryPostProcessor, BeanDefinitionRegistryPostProcessor, DisposableBean 
  private BeanLifecycleWrapperCache cache = new BeanLifecycleWrapperCache(new StandardScopeCache());
  // 将要获取的Bean缓存上
  public Object get(String name, ObjectFactory<?> objectFactory) 
    BeanLifecycleWrapper value = this.cache.put(name, new BeanLifecycleWrapper(name, objectFactory));
    this.locks.putIfAbsent(name, new ReentrantReadWriteLock());
    try 
      return value.getBean();
     catch (RuntimeException e) 
      this.errors.put(name, e);
      throw e;
    
  
    

3 Refresh端点触发时机

触发机制基于事件驱动,发布EnvironmentChangeEvent事件。

当调用/actuator/refresh端点时,执行如下refresh:

@Endpoint(id = "refresh")
public class RefreshEndpoint 
  private ContextRefresher contextRefresher;
  public RefreshEndpoint(ContextRefresher contextRefresher) 
    this.contextRefresher = contextRefresher;
  
  @WriteOperation
  public Collection<String> refresh() 
    Set<String> keys = this.contextRefresher.refresh();
    return keys;
  

调用ContextRefresher#refresh方法

public class ContextRefresher 
  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();
    // 发布事件(处理事件中所有配置类@ConfigurationProperties)
    this.context.publishEvent(new EnvironmentChangeEvent(this.context, keys));
    return keys;
  

在refreshEnvironment方法中会发布一个EnvironmentChangeEvent事件;查看该事件的监听器

public class ConfigurationPropertiesRebinder implements ApplicationContextAware, ApplicationListener<EnvironmentChangeEvent> 
  private ConfigurationPropertiesBeans beans;

  private ApplicationContext applicationContext;

  private Map<String, Exception> errors = new ConcurrentHashMap<>();

  public ConfigurationPropertiesRebinder(ConfigurationPropertiesBeans beans) 
    this.beans = beans;
  
  @ManagedOperation
  public void rebind() 
    this.errors.clear();
    // 遍历所有的配置类(带有@ConfigurationProperties注解的类)  
    for (String name : this.beans.getBeanNames()) 
      // 对每一个bean进行重新绑定  
      rebind(name);
    
  
  @ManagedOperation
  public boolean rebind(String name) 
    if (!this.beans.getBeanNames().contains(name)) 
      return false;
    
    if (this.applicationContext != null) 
      Object bean = this.applicationContext.getBean(name);
      if (AopUtils.isAopProxy(bean)) 
        bean = ProxyUtils.getTargetObject(bean);
      
      if (bean != null) 
        if (getNeverRefreshable().contains(bean.getClass().getName())) 
          return false; // ignore
        
        // 销毁当前的bean  
        this.applicationContext.getAutowireCapableBeanFactory().destroyBean(bean);
        // 初始化Bean  
        this.applicationContext.getAutowireCapableBeanFactory().initializeBean(bean, name);
        return true;
      
    
    return false;
  

  @ManagedAttribute
  public Set<String> getBeanNames() 
    return new HashSet<>(this.beans.getBeanNames());
  
  @Override
  public void onApplicationEvent(EnvironmentChangeEvent event) 
    if (this.applicationContext.equals(event.getSource()) || event.getKeys().equals(event.getSource())) 
      rebind();
    
  

当收到EnvironmentChangeEvent事件后执行rebind方法。在该类中的beans是通过构造函数传过来的,接下来先查看这个对象是如何被构造的

public class ConfigurationPropertiesRebinderAutoConfiguration implements ApplicationContextAware, SmartInitializingSingleton 
  @Bean
  @ConditionalOnMissingBean(search = SearchStrategy.CURRENT)
  public static ConfigurationPropertiesBeans configurationPropertiesBeans() 
    return new ConfigurationPropertiesBeans();
  

  @Bean
  @ConditionalOnMissingBean(search = SearchStrategy.CURRENT)
  public ConfigurationPropertiesRebinder configurationPropertiesRebinder(ConfigurationPropertiesBeans beans) 
    ConfigurationPropertiesRebinder rebinder = new ConfigurationPropertiesRebinder(beans);
    return rebinder;
  

在这个自动配置类中创建了
ConfigurationPropertiesRebinder并且将ConfigurationPropertiesBeans 注入。ConfigurationPropertiesBeans是个BeanPostProcessor处理器

@Component
public class ConfigurationPropertiesBeans
        implements BeanPostProcessor, ApplicationContextAware 
    // 添加有@ConfigurationProperties注解的bean都将保存在该集合中
    private Map<String, ConfigurationPropertiesBean> beans = new HashMap<>();
    private ApplicationContext applicationContext;
    private ConfigurableListableBeanFactory beanFactory;
    private String refreshScope;
    private boolean refreshScopeInitialized;
    private ConfigurationPropertiesBeans parent;
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName)
            throws BeansException 
        if (isRefreshScoped(beanName)) 
            return bean;
        
        ConfigurationPropertiesBean propertiesBean = ConfigurationPropertiesBean
                .get(this.applicationContext, bean, beanName);
        if (propertiesBean != null) 
            this.beans.put(beanName, propertiesBean);
        
        return bean;
    
    private boolean isRefreshScoped(String beanName) 
        if (this.refreshScope == null && !this.refreshScopeInitialized) 
            this.refreshScopeInitialized = true;
            for (String scope : this.beanFactory.getRegisteredScopeNames()) 
                if (this.beanFactory.getRegisteredScope(
                        scope) instanceof org.springframework.cloud.context.scope.refresh.RefreshScope) 
                    this.refreshScope = scope;
                    break;
                
            
        
        if (beanName == null || this.refreshScope == null) 
            return false;
        
        return this.beanFactory.containsBeanDefinition(beanName) && this.refreshScope.equals(this.beanFactory.getBeanDefinition(beanName).getScope());
    
    public Set<String> getBeanNames() 
        return new HashSet<String>(this.beans.keySet());
    

该BeanPostProcessor的
postProcessBeforeInitialization方法执行在初始化bean的时候执行在这里就会判断当前的Bean是否是RefreshScope Bean。

在isRefreshScoped方法中遍历注册的所有Scope并且判断是否是有RefreshScope,先从注册的所有Scope中查找RefreshScope,如果没有返回false,如果是则返回true。如果isRefreshScoped方法返回的false就判断当前Bean是否有@ConfigurationProperties注解如果有会被包装成
ConfigurationPropertiesBean存入当前的beans集合中(当有refresh发生时会重新绑定这些bean)。接下来继续进入到上面的ConfigurationPropertiesRebinder#rebind方法中。

rebind方法中会对所有带有@ConfigurationProperties注解的类进行刷新,rebind方法中会对bean进行销毁和初始化。

this.applicationContext.getAutowireCapableBeanFactory().destroyBean(bean);
this.applicationContext.getAutowireCapableBeanFactory().initializeBean(bean, name);

4 RefreshScope刷新处理

当刷新refresh被调用后会执行RefreshScope#refreshAll方法

public class ContextRefresher 
  public synchronized Set<String> refresh() 
    Set<String> keys = refreshEnvironment();
    this.scope.refreshAll();
    return keys;
  

RefreshScope#refreshAll方法

public void refreshAll() 
  // 调用RefreshScope父类的destroy方法
  super.destroy();
  // 发布RefreshScopeRefreshedEvent事件,我们可以写个监听程序监听该事件
  this.context.publishEvent(new RefreshScopeRefreshedEvent());

父类GenriceScope#destroy方法

public class GenericScope implements Scope, BeanFactoryPostProcessor, BeanDefinitionRegistryPostProcessor, DisposableBean 
  @Override
  public void destroy() 
    List<Throwable> errors = new ArrayList<Throwable>();
    Collection<BeanLifecycleWrapper> wrappers = this.cache.clear();
    for (BeanLifecycleWrapper wrapper : wrappers) 
      try 
        Lock lock = this.locks.get(wrapper.getName()).writeLock();
          lock.lock();
          try 
            wrapper.destroy();
           finally 
            lock.unlock();
          
       catch (RuntimeException e) 
        errors.add(e);
      
    
    if (!errors.isEmpty()) 
      throw wrapIfNecessary(errors.get(0));
    
    this.errors.clear();
  


wrapper#destroy方法
private static class BeanLifecycleWrapper 
  private final String name;
  private final ObjectFactory<?> objectFactory;
  private Object bean;
  private Runnable callback;
  BeanLifecycleWrapper(String name, ObjectFactory<?> objectFactory) 
    this.name = name;
    this.objectFactory = objectFactory;
  
  public Object getBean() 
    if (this.bean == null) 
      synchronized (this.name) 
        if (this.bean == null) 
          this.bean = this.objectFactory.getObject();
        
      
    
    return this.bean;
  
  // 该方法就是清空上次创建的对象信息
  public void destroy() 
    if (this.callback == null) 
      return;
    
    synchronized (this.name) 
      Runnable callback = this.callback;
      if (callback != null) 
        callback.run();
      
      this.callback = null;
      this.bean = null;
    
  

当前清空了缓存对象后,下次再进入注入的时候会再次调用ObjectFacotry#getObject方法创建新的对象。

总结:当触发了refresh后,所有的带有@ConfigurationProperties注解的Bean都会自动的刷新并不需要@RefreshScope注解。而有@RefreshScope注解的一般在应用在非配置类上,有成员属性使用@Value注解的,如下:

@RestController
@RequestMapping("/refreshBeanProp")
@RefreshScope
public class RefreshScopeBeanPropController 
    
  @Value("$custom")
  private String custom ;
    
  @GetMapping("/get")
  public String get() 
    return custom ;
  
    

在此种情况下,调用/actuator/refresh 可使custom动态刷新,在ContextRefresher#refresh中将缓存的Bean清空了后重新生成Bean。

以上是关于SpringCloud @RefreshScope实现原理的主要内容,如果未能解决你的问题,请参考以下文章

SpringCloud+@RefreshScope+@Value配置中心动态加载(刷新)配置文件

SpringCloud+@RefreshScope+@Value配置中心动态加载(刷新)配置文件

SpringCloud Config手动刷新及自动刷新

检测 RefreshScope bean 的刷新

Spring Cloud @RefreshScope 原理分析:代理类调用流程

Spring Cloud @RefreshScope 原理分析:扫描 Bean 定义