配置中的@RefreshScope class

@RefreshScope in Configuration class

我有一个 spring 启动应用程序。我正在使用 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(意思是在class级别)注释配置class时,在这个class中声明的bean没有刷新如果他们在 bean 声明中没有自己的@RefreshScope。

这里的bean没有刷新:

@Configuration
@RefreshScope
public class DraftsClientConfiguration {

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

这是来自我的 RefreshListener class 的日志: 我们可以看到在这种情况下,只有一个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 文档,bean 应该通过在配置 class 级别注释 @RefreshScope 来刷新。无需为配置的每个 bean 声明指定 @RefreshScope class。我错过了什么吗?

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

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

你的理解有点偏差,文档里都有说明。

来自 @RefreshScopejavadoc

The implementation involves creating a proxy for every bean in the scope,

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

来自Spring Cloud Reference Guide

@RefreshScope works (technically) on an @Configuration class, but it might lead to surprising behavior. For example, it does not mean that all the @Beans defined in that class are themselves in @RefreshScope. Specifically, anything that depends on those beans cannot rely on them being updated when a refresh is initiated, unless it is itself in @RefreshScope. In that case, it is rebuilt on a refresh and its dependencies are re-injected. At that point, they are re-initialized from the refreshed @Configuration).

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

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