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

原创文章 95 获赞 219 访问量 29万+

猜你喜欢

转载自blog.csdn.net/zimou5581/article/details/102822023