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。

完畢!!!

給個關注+轉發唄謝謝

眾:Springboot實戰案例錦集

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM