配置类中的@RefreshScope

Posted

技术标签:

【中文标题】配置类中的@RefreshScope【英文标题】:@RefreshScope in Configuration class 【发布时间】:2019-11-11 22:58:36 【问题描述】:

我有一个 Spring Boot 应用程序。我正在使用 Spring Cloud Config 来外部化属性 - 通过 Git。一切正常。 我希望在发出执行器刷新端点时刷新 bean。通过执行以下操作,Bean 会按预期急切地刷新:

@EventListener
public void onRefreshScopeRefreshed(final RefreshScopeRefreshedEvent event) 
    logger.info("Received Refresh event. Refreshing all beans...");
    for (String beanName : applicationContext.getBeanDefinitionNames()) 
        Class<?> beanClass = applicationContext.getBean(beanName).getClass();
        if(beanClass.getName().contains("SpringCGLIB")) 
            logger.info("Proxied bean: bean name: " + beanName + " - Bean class: " + applicationContext.getBean(beanName).getClass());
         else 
            logger.info("Regular Bean: Bean name: " + beanName + " - Bean class: " + applicationContext.getBean(beanName).getClass());
        
        applicationContext.getBean(beanName).getClass(); // to cause refresh eagerly
    

唯一没有按预期工作的是,当我使用 @refreshScope 注释配置类时(意味着在类级别),如果在此类中声明的 bean 在 bean 声明中没有自己的 @RefreshScope,则不会刷新它们。

这里的bean没有刷新:

@Configuration
@RefreshScope
public class DraftsClientConfiguration 

    @Bean
    MyBean aBean() 
        return new MyBean();
    

这是我的 RefreshListener 类的日志: 我们可以看到,在这种情况下,只有一个 bean 没有被代理。

RefreshListener - Regular Bean: Bean name: draftsServiceClient - Bean class: class com.citi.qi.athena.drafts.DraftsServiceClient

但是这里的 bean 被刷新了:

@Configuration
public class DraftsClientConfiguration 

    @RefreshScope
    @Bean
    MyBean aBean() 
        return new MyBean();
    

在第二种情况下,我们有两个 bean(应该是这种情况吗?),一个被代理,一个没有被代理。

RefreshListener - Regular Bean: Bean name: scopedTarget.draftsServiceClient - Bean class: class com.citi.qi.athena.drafts.DraftsServiceClient
RefreshListener - Proxied bean: bean name: draftsServiceClient - Bean class: class com.citi.qi.athena.drafts.DraftsServiceClient$$EnhancerBySpringCGLIB$$bbfd1caf

根据 Spring doc,bean 应该通过在配置类级别注释 @RefreshScope 来刷新。无需为配置类的每个 bean 声明指定 @RefreshScope。我错过了什么吗?

顺便说一句,我通过在 bean 声明中放置断点来检查 bean 是否刷新。

第二个问题:我想我应该只有一个代理 bean 而不是我们在第二种情况下看到的两个 bean?

【问题讨论】:

您将拥有 2 个 bean 实例、1 个代理和 1 个实际实例。代理将在刷新后继续存在,并将委托给新创建的实际实例。将@RefreshScope 放在配置上确实会刷新配置,但不会刷新它创建的bean。您需要明确定义要刷新的 bean。这可以通过将所需的类注释为@RefreshScope 或将@Bean 方法注释为@RefreshScope 来完成。 【参考方案1】:

您的理解有点偏离,所有这些都在文档中说明。

来自javadoc 的@RefreshScope

实现涉及为范围内的每个 bean 创建一个代理,

因此您将获得 2 个 bean 实例。 1 个代理,它将实际包装 bean 的完整实例。刷新时,代理将继续存在,实际实例将被替换。

来自Spring Cloud Reference Guide:

@RefreshScope (技术上)在@Configuration 类上工作,但它可能会导致令人惊讶的行为。例如,这并不意味着该类中定义的所有@Beans 本身都在@RefreshScope 中。具体来说,任何依赖于这些 bean 的东西都不能依赖它们在启动刷新时被更新,除非它本身在 @RefreshScope 中。在这种情况下,它会在刷新时重建,并重新注入其依赖项。此时,它们会从刷新后的@Configuration 重新初始化。

因此,虽然在技术上可能使用对这些 bean 的引用但可能不会刷新,除非它们也被标记为 @RefreshScope

简而言之,解决方案是通过将类注释为@RefreshScope@Bean 方法来显式标记需要在@RefreshScope 中的bean。

【讨论】:

以上是关于配置类中的@RefreshScope的主要内容,如果未能解决你的问题,请参考以下文章

配置中心@ConfigurationProperties+@RefreshScope 动态配置

解决 @RefreshScope 导致定时任务注解 @Scheduled 失效

解决 @RefreshScope 导致定时任务注解 @Scheduled 失效

解决 @RefreshScope 导致定时任务注解 @Scheduled 失效

@RefreshScope 加在 Quartz 触发器类导致异常问题分析

config-server中@RefreshScope的"陷阱"