欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解SpringCloud是如何動(dòng)態(tài)更新配置的

 更新時(shí)間:2023年06月04日 08:56:33   作者:西直門三太子  
spring cloud在config配置管理的基礎(chǔ)上,提供了consul config的配置管理和動(dòng)態(tài)監(jiān)聽,那么這里面到底是怎樣實(shí)現(xiàn)的,本文將為你揭秘,感興趣的小伙伴可以跟著小伙伴一起來學(xué)習(xí)

前言

對于單體應(yīng)用架構(gòu)來說,會(huì)使用配置文件管理我們的配置,這就是之前項(xiàng)目中的application.properties或application.yml。

如果需要在多環(huán)境下使用,傳統(tǒng)的做法是復(fù)制這些文件命名為application-xxx.properties,并且在啟動(dòng)時(shí)配置spring.profiles.active={profile}來指定環(huán)境。

在微服務(wù)架構(gòu)下我們可能會(huì)有很多的微服務(wù),所以要求的不只是在各自微服務(wù)中進(jìn)行配置,我們需要將所有的配置放在統(tǒng)一平臺上進(jìn)行操作,不同的環(huán)境進(jìn)行不同的配置,運(yùn)行期間動(dòng)態(tài)調(diào)整參數(shù)等等。 

于是Spring Cloud為我們提供了一個(gè)統(tǒng)一的配置管理,那就是Spring Cloud Config。

spring cloud config簡介

它為分布式系統(tǒng)外部配置提供了服務(wù)器端和客戶端的支持,它包括config server端和 config client端兩部分

  • Config server端是一個(gè)可以橫向擴(kuò)展、集中式的配置服務(wù)器,它用于集中管理應(yīng)用程序各個(gè)環(huán)境下的配置,默認(rèn) 使用Git存儲配置內(nèi)容
  • Config client 是config server的客戶端,用于操作存儲在server中的配置屬性

啟動(dòng)加載擴(kuò)展點(diǎn)

spring boot提供在 META-INF/spring.factories 文件中增加配置,來實(shí)現(xiàn)一些程序中預(yù)定義的擴(kuò)展點(diǎn)。

通過這種方式配置的擴(kuò)展點(diǎn)好處是不局限于某一種接口的實(shí)現(xiàn),而是同一類別的實(shí)現(xiàn)。

我們查看 spring-cloud-context 包中的 spring.factories 文件,如下所示:

# AutoConfiguration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration,\
org.springframework.cloud.autoconfigure.LifecycleMvcEndpointAutoConfiguration,\
org.springframework.cloud.autoconfigure.RefreshAutoConfiguration,\
org.springframework.cloud.autoconfigure.RefreshEndpointAutoConfiguration,\
org.springframework.cloud.autoconfigure.WritableEnvironmentEndpointAutoConfiguration
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.cloud.bootstrap.BootstrapApplicationListener,\
org.springframework.cloud.bootstrap.LoggingSystemShutdownListener,\
org.springframework.cloud.context.restart.RestartListener
# Bootstrap components
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration,\
org.springframework.cloud.bootstrap.encrypt.EncryptionBootstrapConfiguration,\
org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.cloud.util.random.CachedRandomPropertySourceAutoConfiguration

可以看到BootstrapConfiguration下面有一個(gè)item,PropertySourceBootstrapConfiguration,進(jìn)入其代碼,查看即繼承關(guān)系,發(fā)現(xiàn)其實(shí)現(xiàn)了 ApplicationContextInitializer 接口,其目的就是在應(yīng)用程序上下文初始化的時(shí)候做一些額外的操作。 

在 Bootstrap 階段,會(huì)通過 Spring Ioc 的整個(gè)生命周期來初始化所有通過key為org.springframework.cloud.bootstrap.BootstrapConfiguration 在 spring.factories 中配置的 Bean。

初始化的過程中,會(huì)獲取所有 ApplicationContextInitializer 類型的 Bean,并設(shè)置回SpringApplication主流程當(dāng)中。通過在 SpringApplication 的主流程中回調(diào)這些 ApplicationContextInitializer 的實(shí)例,做一些初始化的操作,即調(diào)用initialize方法。

下面我們就來看看PropertySourceBootstrapConfiguration這個(gè)方法:

@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
CompositePropertySource composite = new CompositePropertySource(
    BOOTSTRAP_PROPERTY_SOURCE_NAME);
AnnotationAwareOrderComparator.sort(this.propertySourceLocators);
boolean empty = true;
ConfigurableEnvironment environment = applicationContext.getEnvironment();
for (PropertySourceLocator locator : this.propertySourceLocators) {
  PropertySource<?> source = null;
  //回調(diào)所有實(shí)現(xiàn)PropertySourceLocator接口實(shí)例的locate方法,
  source = locator.locate(environment);
  if (source == null) {
    continue;
  }
  composite.addPropertySource(source);
  empty = false;
}
if (!empty) {
//從當(dāng)前Enviroment中獲取 propertySources
  MutablePropertySources propertySources = environment.getPropertySources();
  //省略...
  //將composite中的PropertySource添加到當(dāng)前應(yīng)用上下文的propertySources中
  insertPropertySources(propertySources, composite);
  //省略...
}

在這個(gè)方法中會(huì)回調(diào)所有實(shí)現(xiàn) PropertySourceLocator 接口實(shí)例的locate方法, locate 方法返回一個(gè) PropertySource 的實(shí)例,統(tǒng)一add到CompositePropertySource實(shí)例中。如果 composite 中有新加的PropertySource,最后將composite中的PropertySource添加到當(dāng)前應(yīng)用上下文的propertySources中。

SpringCloudConsul的配置加載

正如上面說的,在 Bootstrap 階段,會(huì)通過 Spring Ioc 的整個(gè)生命周期來初始化所有通過key為org.springframework.cloud.bootstrap.BootstrapConfiguration 在 spring.factories 中配置的 Bean。同樣的在spring.factories文件中:

# Auto Configuration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.consul.config.ConsulConfigAutoConfiguration
# Bootstrap Configuration
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.consul.config.ConsulConfigBootstrapConfiguration

我們確實(shí)看到了又這樣一個(gè)key存在,對應(yīng)value為ConsulConfigBootstrapConfiguration類,我們看看該類的實(shí)現(xiàn):

@Configuration(proxyBeanMethods = false)
@ConditionalOnConsulEnabled
public class ConsulConfigBootstrapConfiguration {
  @Configuration(proxyBeanMethods = false)
  @EnableConfigurationProperties
  @Import(ConsulAutoConfiguration.class)
  @ConditionalOnProperty(name = "spring.cloud.consul.config.enabled",
      matchIfMissing = true)
  protected static class ConsulPropertySourceConfiguration {
    @Autowired
    private ConsulClient consul;
    @Bean
    @ConditionalOnMissingBean
    public ConsulConfigProperties consulConfigProperties() {
      return new ConsulConfigProperties();
    }
    @Bean
    public ConsulPropertySourceLocator consulPropertySourceLocator(
        ConsulConfigProperties consulConfigProperties) {
      return new ConsulPropertySourceLocator(this.consul, consulConfigProperties);
    }
  }
}

我們看到,這里只是注入了一些bean,我們注意下ConsulPropertySourceLocator這個(gè)類。

正如上面說的,SpringCloudConfig在啟動(dòng)的時(shí)候會(huì)回調(diào)所有實(shí)現(xiàn) PropertySourceLocator 接口實(shí)例的locate方法,consul就是實(shí)現(xiàn)了PropertySourceLocator接口,具體類為ConsulPropertySourceLocator,實(shí)現(xiàn)了locate方法:

@Override
@Retryable(interceptor = "consulRetryInterceptor")
public PropertySource<?> locate(Environment environment) {
  if (environment instanceof ConfigurableEnvironment) {
    ConfigurableEnvironment env = (ConfigurableEnvironment) environment;
    String appName = this.properties.getName();
    if (appName == null) {
      appName = env.getProperty("spring.application.name");
    }
    List<String> profiles = Arrays.asList(env.getActiveProfiles());
    String prefix = this.properties.getPrefix();
    List<String> suffixes = new ArrayList<>();
    // 不是文件類型的時(shí)候,后綴為 /,否則就是配置文件的后綴
    if (this.properties.getFormat() != FILES) {
      suffixes.add("/");
    } else {
      suffixes.add(".yml");
      suffixes.add(".yaml");
      suffixes.add(".properties");
    }
    // 路徑
    String defaultContext = getContext(prefix, this.properties.getDefaultContext());
    for (String suffix : suffixes) {
      this.contexts.add(defaultContext + suffix);
    }
    // 追加環(huán)境及文件類型
    for (String suffix : suffixes) {
      addProfiles(this.contexts, defaultContext, profiles, suffix);
    }
    String baseContext = getContext(prefix, appName);
    // 應(yīng)用名稱前綴
    for (String suffix : suffixes) {
      this.contexts.add(baseContext + suffix);
    }
    for (String suffix : suffixes) {
      addProfiles(this.contexts, baseContext, profiles, suffix);
    }
    Collections.reverse(this.contexts);
    CompositePropertySource composite = new CompositePropertySource("consul");
    for (String propertySourceContext : this.contexts) {
      try {
        ConsulPropertySource propertySource = null;
        if (this.properties.getFormat() == FILES) {
          // 獲取值
          Response<GetValue> response = this.consul.getKVValue(propertySourceContext, this.properties.getAclToken());
          // 添加當(dāng)前索引
          addIndex(propertySourceContext, response.getConsulIndex());
          // 如果值不為空,則更新值并初始化
          if (response.getValue() != null) {
            ConsulFilesPropertySource filesPropertySource = new ConsulFilesPropertySource(propertySourceContext, this.consul, this.properties);
            // 解析配置內(nèi)容
            filesPropertySource.init(response.getValue());
            propertySource = filesPropertySource;
          }
        } else {
          propertySource = create(propertySourceContext, this.contextIndex);
        }
        if (propertySource != null) {
          composite.addPropertySource(propertySource);
        }
      } catch (Exception e) {
        if (this.properties.isFailFast()) {
          log.error("Fail fast is set and there was an error reading configuration from consul.");
          ReflectionUtils.rethrowRuntimeException(e);
        } else {
          log.warn("Unable to load consul config from " + propertySourceContext, e);
        }
      }
    }
    return composite;
  }
  return null;
}

獲取配置時(shí),根據(jù)應(yīng)用名稱,路徑,環(huán)境及配置類型拼接相應(yīng)的路徑,然后調(diào)用 Consul 獲取 KV 值的接口,獲取相應(yīng)的配置,根據(jù)類型解析后放入環(huán)境中

配置動(dòng)態(tài)刷新

感知到外部化配置的變更這部分代碼的操作是需要用戶來完成的。Spring Cloud Config 只提供了具備外部化配置可動(dòng)態(tài)刷新的能力,并不具備自動(dòng)感知外部化配置發(fā)生變更的能力。

比如如果你的配置是基于Mysql來實(shí)現(xiàn)的,那么在代碼里面肯定要有能力感知到配置發(fā)生變化了,然后再顯示的調(diào)用 ContextRefresher 的 refresh方法,從而完成外部化配置的動(dòng)態(tài)刷新(只會(huì)刷新使用RefreshScope注解的Bean)。

下面我們來看看config框架是怎么進(jìn)行動(dòng)態(tài)刷新的?主要類是這個(gè)ContextRefresher,刷新方法如下:

public synchronized Set refresh() {
    Map<String, Object> before = extract(
            this.context.getEnvironment().getPropertySources());
    //1、加載最新的值,并替換Envrioment中舊值
    addConfigFilesToEnvironment();
    Set<String> keys = changes(before,
            extract(this.context.getEnvironment().getPropertySources())).keySet();
    this.context.publishEvent(new EnvironmentChangeEvent(context, keys));
    //2、將refresh scope中的Bean 緩存失效: 清空
    this.scope.refreshAll();
    return keys;
}

上面ContextRefresher的refresh的方法主要做了兩件事:

  • 1、觸發(fā)PropertySourceLocator的locator方法,需要加載最新的值,并替換 Environment 中舊值
  • 2、Bean中的引用配置值需要重新注入一遍。重新注入的流程是在Bean初始化時(shí)做的操作,那也就是需要將refresh scope中的Bean 緩存失效,當(dāng)再次從refresh scope中獲取這個(gè)Bean時(shí),發(fā)現(xiàn)取不到,就會(huì)重新觸發(fā)一次Bean的初始化過程。

可以看到上面代碼中有這樣一句this.scope.refreshAll(),其中的scope就是RefreshScope。是用來存放scope類型為refresh類型的Bean(即使用RefreshScope注解標(biāo)識的Bean),也就是說當(dāng)一個(gè)Bean既不是singleton也不是prototype時(shí),就會(huì)從自定義的Scope中去獲取(Spring 允許自定義Scope),然后調(diào)用Scope的get方法來獲取一個(gè)實(shí)例,Spring Cloud 正是擴(kuò)展了Scope,從而控制了整個(gè) Bean 的生命周期。當(dāng)配置需要?jiǎng)討B(tài)刷新的時(shí)候, 調(diào)用this.scope.refreshAll()這個(gè)方法,就會(huì)將整個(gè)RefreshScope的緩存清空,完成配置可動(dòng)態(tài)刷新的可能。

注:關(guān)于ContextRefreshRefreshScope的初始化配置是在RefreshAutoConfiguration類中完成的。而RefreshAutoConfiguration類初始化的入口是在spring-cloud-context中的META-INF/spring.factories中配置的。從而完成整個(gè)和動(dòng)態(tài)刷新相關(guān)的Bean的初始化操作。

SpringCloudConsul的配置刷新

Consul 監(jiān)聽配置是通過定時(shí)任務(wù)實(shí)現(xiàn)的,涉及的類為ConfigWatch

public class ConfigWatch implements ApplicationEventPublisherAware, SmartLifecycle {}

該類的初始化是在 org.springframework.cloud.consul.config.ConsulConfigAutoConfiguration 中實(shí)現(xiàn)的:

@Bean
@ConditionalOnProperty(name = "spring.cloud.consul.config.watch.enabled", matchIfMissing = true)
public ConfigWatch configWatch(ConsulConfigProperties properties,
                               ConsulPropertySourceLocator locator,
                               ConsulClient consul,
                               @Qualifier(CONFIG_WATCH_TASK_SCHEDULER_NAME) TaskScheduler taskScheduler) {
  return new ConfigWatch(properties, consul, locator.getContextIndexes(), taskScheduler);
}

我們看到ConfigWatch 類實(shí)現(xiàn)了 ApplicationEventPublisherAware 和 SmartLifecycle 接口. 當(dāng)應(yīng)用啟動(dòng)后,會(huì)調(diào)用 其實(shí)現(xiàn)的SmartLifecycle 的 start 方法,然后初始化配置監(jiān)聽,通過向線程池添加一個(gè)定時(shí)任務(wù),實(shí)現(xiàn)配置的定時(shí)拉取,定時(shí)任務(wù)默認(rèn)周期是 1s

@Override
public void start() {
  if (this.running.compareAndSet(false, true)) {
    this.watchFuture = this.taskScheduler.scheduleWithFixedDelay(
      this::watchConfigKeyValues, this.properties.getWatch().getDelay());
  }
}

1、發(fā)布事件

定時(shí)任務(wù)的監(jiān)聽邏輯如下:

// Timed 是 Prometheus 的監(jiān)控
@Timed("consul.watch-config-keys")
public void watchConfigKeyValues() {
  if (this.running.get()) {
    // 遍歷所有的配置的 key
    for (String context : this.consulIndexes.keySet()) {
      // turn the context into a Consul folder path (unless our config format
      // are FILES)
      if (this.properties.getFormat() != FILES && !context.endsWith("/")) {
        context = context + "/";
      }
      // 根據(jù)配置返回的 index 判斷是否發(fā)生變化
      try {
        Long currentIndex = this.consulIndexes.get(context);
        if (currentIndex == null) {
          currentIndex = -1L;
        }
        log.trace("watching consul for context '" + context + "' with index " + currentIndex);
        // use the consul ACL token if found
        String aclToken = this.properties.getAclToken();
        if (StringUtils.isEmpty(aclToken)) {
          aclToken = null;
        }
        // 獲取指定的 key
        Response<List<GetValue>> response = this.consul.getKVValues(context, aclToken, new QueryParams(this.properties.getWatch().getWaitTime(), currentIndex));
        // if response.value == null, response was a 404, otherwise it was a
        // 200
        // reducing churn if there wasn't anything
        if (response.getValue() != null && !response.getValue().isEmpty()) {
          Long newIndex = response.getConsulIndex();
          // 判斷 key 的 index 是否相等,如果發(fā)生變化,則發(fā)出 RefreshEvent 事件
          if (newIndex != null && !newIndex.equals(currentIndex)) {
            // don't publish the same index again, don't publish the first
            // time (-1) so index can be primed
            // 沒有發(fā)布過這個(gè) index 的事件,且不是第一次發(fā)布
            if (!this.consulIndexes.containsValue(newIndex) && !currentIndex.equals(-1L)) {
              log.trace("Context " + context + " has new index " + newIndex);
              // 發(fā)送事件
              RefreshEventData data = new RefreshEventData(context, currentIndex, newIndex);
              this.publisher.publishEvent(new RefreshEvent(this, data, data.toString()));
            } else if (log.isTraceEnabled()) {
              log.trace("Event for index already published for context " + context);
            }
            this.consulIndexes.put(context, newIndex);
          } else if (log.isTraceEnabled()) {
            log.trace("Same index for context " + context);
          }
        } else if (log.isTraceEnabled()) {
          log.trace("No value for context " + context);
        }
      } catch (Exception e) {
        // only fail fast on the initial query, otherwise just log the error
        if (this.firstTime && this.properties.isFailFast()) {
          log.error("Fail fast is set and there was an error reading configuration from consul.");
          ReflectionUtils.rethrowRuntimeException(e);
        } else if (log.isTraceEnabled()) {
          log.trace("Error querying consul Key/Values for context '" + context + "'", e);
        } else if (log.isWarnEnabled()) {
          // simplified one line log message in the event of an agent
          // failure
          log.warn("Error querying consul Key/Values for context '" + context + "'. Message: " + e.getMessage());
        }
      }
    }
  }
  this.firstTime = false;
}

監(jiān)聽時(shí)會(huì)遍歷所有的key,根據(jù) key 從 Consul 獲取相應(yīng)的數(shù)據(jù),判斷 Index 是否發(fā)生變化,如果發(fā)生變化,則發(fā)送 RefreshEvent 事件,需要手動(dòng)實(shí)現(xiàn)事件監(jiān)聽以響應(yīng)配置變化。 

至于spring是怎樣發(fā)布事件,監(jiān)聽者又是怎樣接收到的,這里面的細(xì)節(jié)后續(xù)有時(shí)間再詳細(xì)剖析。

2、事件監(jiān)聽

現(xiàn)在我們主要來看下RefreshEvent發(fā)出去之后,監(jiān)聽者的邏輯。

通過函數(shù)調(diào)用棧,我們找到了這樣一個(gè)監(jiān)聽者RefreshEventListener

@Override
public void onApplicationEvent(ApplicationEvent event) {
  if (event instanceof ApplicationReadyEvent) {
    handle((ApplicationReadyEvent) event);
  }
  else if (event instanceof RefreshEvent) {
    handle((RefreshEvent) event);
  }
}

我們知道,在spring中,監(jiān)聽者都需要實(shí)現(xiàn)這樣一個(gè)方法onApplicationEvent,該方法中我們發(fā)現(xiàn)有這樣一個(gè)分支

else if (event instanceof RefreshEvent) {
  handle((RefreshEvent) event);
}

這個(gè)事件就是上面發(fā)出來的,因此這里能夠監(jiān)聽到,然后執(zhí)行回調(diào)方法handle

public void handle(RefreshEvent event) {
  if (this.ready.get()) { // don't handle events before app is ready
    log.debug("Event received " + event.getEventDesc());
    Set<String> keys = this.refresh.refresh();
    log.info("Refresh keys changed: " + keys);
  }
}
public synchronized Set<String> refresh() {
  Set<String> keys = refreshEnvironment();
  this.scope.refreshAll();
  return keys;
}

主要的邏輯在refreshEnvironment方法中:

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();
  this.context.publishEvent(new EnvironmentChangeEvent(this.context, keys));
  return keys;
}

我們知道this.context.getEnvironment().getPropertySources()是獲取env中的所有配置源,然后將其交給extract進(jìn)行處理。extract方法就是將配置源中的各種格式的配置,比如map、yml、properties類型等等,統(tǒng)一轉(zhuǎn)換為map類型,這樣就可以通過統(tǒng)一key-value形式獲取到任意想要的配置值。上面這段代碼的主要邏輯就是:

  • 1、獲取所有的舊的(更新之前的)配置值
  • 2、重新通過應(yīng)用初始方式更新所有的配置值addConfigFilesToEnvironment
  • 3、將最新的值跟舊的值進(jìn)行對比,找出所有的更新過的key
  • 4、重新發(fā)布配置變更時(shí)間EnvironmentChangeEvent,將更新過的key傳遞給該事件

3、Env配置更新

下面來說下第二點(diǎn):重新通過應(yīng)用初始方式更新所有的配置值addConfigFilesToEnvironment,

/* For testing. */ ConfigurableApplicationContext addConfigFilesToEnvironment() {
  ConfigurableApplicationContext capture = null;
  try {
    StandardEnvironment environment = copyEnvironment(
        this.context.getEnvironment());
    SpringApplicationBuilder builder = new SpringApplicationBuilder(Empty.class)
        .bannerMode(Mode.OFF).web(WebApplicationType.NONE)
        .environment(environment);
    // Just the listeners that affect the environment (e.g. excluding logging
    // listener because it has side effects)
    builder.application()
        .setListeners(Arrays.asList(new BootstrapApplicationListener(),
            new ConfigFileApplicationListener()));
    capture = builder.run();
    if (environment.getPropertySources().contains(REFRESH_ARGS_PROPERTY_SOURCE)) {
      environment.getPropertySources().remove(REFRESH_ARGS_PROPERTY_SOURCE);
    }
    MutablePropertySources target = this.context.getEnvironment()
        .getPropertySources();
    String targetName = null;
    for (PropertySource<?> source : environment.getPropertySources()) {
      String name = source.getName();
      if (target.contains(name)) {
        targetName = name;
      }
      if (!this.standardSources.contains(name)) {
        if (target.contains(name)) {
          target.replace(name, source);
        }
        else {
          if (targetName != null) {
            target.addAfter(targetName, source);
            // update targetName to preserve ordering
            targetName = name;
          }
          else {
            // targetName was null so we are at the start of the list
            target.addFirst(source);
            targetName = name;
          }
        }
      }
    }
  }
    ......
|

我們看到有這樣一句SpringApplicationBuilder,這里它是生成了一個(gè)spring應(yīng)用對象的生成器,然后執(zhí)行它的run方法,

也就是說,這里會(huì)重新執(zhí)行一遍spring的啟動(dòng)流程,所有的啟動(dòng)初始類都會(huì)重新執(zhí)行,包括上面提到的ConsulPropertySourceLocator類的locate方法,這里就會(huì)再次向consul server發(fā)起請求獲取最新的配置數(shù)據(jù),寫入env中。

因此后面通過this.context.getEnvironment().getPropertySources()得到的就是最新的配置源了。同時(shí)業(yè)務(wù)中也可以通過context.getEnvironment().getProperty(key)拿到任意key的最新值了。

刷新scope域

在上面的refresh方法中,我們還剩下這樣一句沒有講解:

this.scope.refreshAll();

這里主要就是刷新spring容器該scope類型下的所有bean,就可以通過@RefreshScope的bean實(shí)例的get方法獲取到最新的值了。前提條件是我們需要監(jiān)聽這個(gè)事件RefreshScopeRefreshedEvent

@EventListener(classes = RefreshScopeRefreshedEvent.class)
public void updateChange(RefreshScopeRefreshedEvent event) {
  //這里獲取到的新的值
  String pwd = redisProperties.getPassword();
  System.out.print("new pwd: " + pwd);
}

上面的EnvironmentChangeEvent這個(gè)事件發(fā)生時(shí),@RefreshScope的bean實(shí)例還是老的bean,在這個(gè)事件里拿到的還是老的值:

@EventListener(classes = EnvironmentChangeEvent.class)
public void updateChange(EnvironmentChangeEvent event) {
  Set<String> updatedKeys = event.getKeys();
  System.out.print(updatedKeys);
  for (String key : updatedKeys) {
    if (key.equals("redis.password")) {
      System.out.print("new password: " + context.getEnvironment().getProperty(key));
      //    do something
    }
  }
  //這里獲取到的還是舊的值
  String pwd = redisProperties.getPassword();
  System.out.print("old pwd: " + pwd);
}

具體是怎樣刷新scope域的,后面有時(shí)間再專門講解。

以上就是詳解SpringCloud是如何動(dòng)態(tài)更新配置的的詳細(xì)內(nèi)容,更多關(guān)于SpringCloud動(dòng)態(tài)更新配置的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • SpringMVC?中的視圖使用?JSP的過程

    SpringMVC?中的視圖使用?JSP的過程

    本文介紹了如何在?SpringMVC?中使用?JSP?視圖,包括如何創(chuàng)建?JSP?視圖、配置?JSP?視圖解析器、以及如何在控制器方法中使用JSP視圖,本文給大家介紹的非常詳細(xì),需要的朋友參考下吧
    2023-07-07
  • struts2中simple主題下<s:fieldError>標(biāo)簽?zāi)J(rèn)樣式的移除方法

    struts2中simple主題下<s:fieldError>標(biāo)簽?zāi)J(rèn)樣式的移除方法

    這篇文章主要給大家介紹了關(guān)于struts2中simple主題下<s:fieldError>標(biāo)簽?zāi)J(rèn)樣式的移除方法,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-10-10
  • java遞歸菜單樹轉(zhuǎn)換成pojo對象

    java遞歸菜單樹轉(zhuǎn)換成pojo對象

    這篇文章介紹了java遞歸菜單樹轉(zhuǎn)換成pojo對象的具體實(shí)現(xiàn),有需要的朋友可以參考一下
    2013-08-08
  • SWT(JFace)體驗(yàn)之復(fù)制粘貼

    SWT(JFace)體驗(yàn)之復(fù)制粘貼

    SWT(JFace)體驗(yàn)之復(fù)制粘貼
    2009-06-06
  • 淺析Spring?中?Bean?的理解與使用

    淺析Spring?中?Bean?的理解與使用

    這篇文章主要介紹了Spring?中?Bean?的理解與使用,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-03-03
  • Spring5路徑匹配器PathPattern解析

    Spring5路徑匹配器PathPattern解析

    這篇文章主要介紹了Spring5路徑匹配器PathPattern,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • springboot?整合表達(dá)式計(jì)算引擎?Aviator?使用示例詳解

    springboot?整合表達(dá)式計(jì)算引擎?Aviator?使用示例詳解

    本文詳細(xì)介紹了Google?Aviator?這款高性能、輕量級的?Java?表達(dá)式求值引擎,并通過詳細(xì)的代碼操作演示了相關(guān)API的使用以及如何在springboot項(xiàng)目中進(jìn)行集成,感興趣的朋友一起看看吧
    2024-08-08
  • Spring Boot參數(shù)校驗(yàn)及分組校驗(yàn)的使用教程

    Spring Boot參數(shù)校驗(yàn)及分組校驗(yàn)的使用教程

    在日常的開發(fā)中,參數(shù)校驗(yàn)是非常重要的一個(gè)環(huán)節(jié),嚴(yán)格參數(shù)校驗(yàn)會(huì)減少很多出bug的概率,增加接口的安全性,下面這篇文章主要給大家介紹了關(guān)于Spring Boot參數(shù)校驗(yàn)及分組校驗(yàn)使用的相關(guān)資料,需要的朋友可以參考下
    2021-08-08
  • SpringMVC中Controller層獲取前端請求參數(shù)的方式匯總

    SpringMVC中Controller層獲取前端請求參數(shù)的方式匯總

    這篇文章主要介紹了SpringMVC中Controller層獲取前端請求參數(shù)的幾種方式,本文通過示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-08-08
  • RabbitMQ使用SpringAMQP的配置方法

    RabbitMQ使用SpringAMQP的配置方法

    這篇文章主要介紹了RabbitMQ使用SpringAMQP的配置方法,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2024-03-03

最新評論