SpringCloud配置熱更新@RefreshScope,以及沒有出現/refresh的動態刷新地址,訪問404的解決辦法

1. 什麼是@RefreshScope

在使用SpringCloud的高可用配置中心的時候,發現了這個@RefreshScope的註解
官方對這個註解的註釋如下:

Note that all beans in this scope are only initialized when first accessed, so the scope forces lazy initialization semantics. The implementation involves creating a proxy for every bean in the scope, so there is a flag

If a bean is refreshed then the next time the bean is accessed (i.e. a method is executed) a new instance is created. All lifecycle methods are applied to the bean instances, so any destruction callbacks that were registered in the bean factory are called when it is refreshed, and then the initialization callbacks are invoked as normal when the new instance is created. A new bean instance is created from the original bean definition, so any externalized content (property placeholders or expressions in string literals) is re-evaluated when it is created.

翻譯:

在這個範圍內的所有bean 僅在首次訪問時初始化,所以這個範圍強制懶加載。這個註解會給範圍內的每個bean創建個代理對象.

如果刷新bean,則下次訪問bean時(即執行方法)將創建一個新實例。所有生命週期方法都應用於bean實例,因此在刷新時會調用在bean工廠的銷燬回調方法,然後在創建新實例時正常調用初始化回調。從原始bean定義創建新的bean實例,因此在創建時會重新評估任何外化內容(屬性佔位符或字符串文字中的表達式)

所以這段話的重點就是:

  1. 所有@RefreshScope的Bean都是延遲加載的,只有在第一次訪問時纔會初始化
  2. 刷新Bean也是同理,下次訪問時會創建一個新的對象

也就是我們可以動態的更新代碼中引用的配置文件的配置,或者本地的配置

2. 爲什麼RefreshScope能刷新配置

看看org.springframework.cloud.context.scope.refresh.RefreshScope這個類

public class RefreshScope extends GenericScope implements ApplicationContextAware, Ordered {

 public boolean refresh(String name) {
    if (!name.startsWith("scopedTarget.")) {
      name = "scopedTarget." + name;
    }

    if (super.destroy(name)) {
      this.context.publishEvent(new RefreshScopeRefreshedEvent(name));
      return true;
    } else {
      return false;
    }
  }

 public void refreshAll() {
    super.destroy();
    this.context.publishEvent(new RefreshScopeRefreshedEvent());
  }
}

它在調用refresh方法的時候,會去調用工廠摧毀已生成的bean對象
看看它的父類GenericScope:

public class GenericScope implements Scope, BeanFactoryPostProcessor, BeanDefinitionRegistryPostProcessor, DisposableBean {

  private GenericScope.BeanLifecycleWrapperCache cache = new GenericScope.BeanLifecycleWrapperCache(new StandardScopeCache());

 protected boolean destroy(String name) {
    GenericScope.BeanLifecycleWrapper wrapper = this.cache.remove(name);
    if (wrapper != null) {
      Lock lock = ((ReadWriteLock)this.locks.get(wrapper.getName())).writeLock();
      lock.lock();

      try {
        wrapper.destroy();
      } finally {
        lock.unlock();
      }

      this.errors.remove(name);
      return true;
    } else {
      return false;
    }
  }

 public Object get(String name, ObjectFactory<?> objectFactory) {
    GenericScope.BeanLifecycleWrapper value = this.cache.put(name, new GenericScope.BeanLifecycleWrapper(name, objectFactory));
    this.locks.putIfAbsent(name, new ReentrantReadWriteLock());

    try {
      return value.getBean();
    } catch (RuntimeException var5) {
      this.errors.put(name, var5);
      throw var5;
    }
  }

 public Object getBean() {
      if (this.bean == null) {
        String var1 = this.name;
        synchronized(this.name) {
          if (this.bean == null) {
            this.bean = this.objectFactory.getObject();
          }
        }
      }

      return this.bean;
    }
    }

這個類中有一個成員變量BeanLifecycleWrapperCache,用於緩存所有已經生成的Bean,在調用get方法時嘗試從緩存加載,如果沒有的話就生成一個新對象放入緩存,並通過初始化getBean其對應的Bean.

清空緩存後,下次訪問對象時就會重新創建新的對象並放入緩存了。

所以在重新創建新的對象時,也就獲取了最新的配置, 也就達到了配置刷新的目的.

3. SpringCloud2.0以後,沒有/refresh手動調用的刷新配置地址

在原來,只要加入依賴:

<dependency>
   <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
 </dependency>

並且在類上,變量上打上@RefreshScope的註解,在啓動的時候,都會看到

RequestMappingHandlerMapping : Mapped "{/refresh,methods=[post]}" 

也就是SpringCloud暴露了一個接口/refresh來給我們去刷新配置,但是SpringCloud 2.0.0以後,有了改變.

  1. 我們需要在bootstrap.yml裏面加上需要暴露出來的地址
    management:
      endpoints:
        web:
          exposure:
            include: refresh,health
    
  2. 現在的地址也不是/refresh了,而是/actuator/refresh

————————————————

PS: 還可以使用 spring cloud bus 實現熱更新

————————————————
版權聲明:本文爲CSDN博主「zzzgd_666」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/zzzgd_666/article/details/84322947

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章