Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

@RefreshScope in Configuration class

I have a spring boot application. I am using Spring Cloud Config to externalize properties - through Git. Everything works fine. I would like the beans to be refreshed when I issue the actuator refresh endpoint. Beans are refreshed eagerly as expected by doing the following:

@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
    }
}

The only thing not working as expected is when I annotate a Configuration class with @refreshScope (meaning at the class level), beans declared in this class are not refreshed if they don't have themselves @RefreshScope in the bean declaration.

Here the bean is not refreshed:

@Configuration
@RefreshScope
public class DraftsClientConfiguration {

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

Here is the log from my RefreshListener class: We can see that in this case, there is only one bean that is not proxied.

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

But here the bean is refreshed:

@Configuration
public class DraftsClientConfiguration {

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

In this second case, we have two beans (should it be the case?), one proxied and one not proxied.

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

According to Spring doc, beans should be refreshed by annotating @RefreshScope at the configuration class level. There is no need to specify @RefreshScope for every bean declaration of the configuration class. Am I missing something?

By the way, I am checking if the bean is refreshed or not by putting a breakpoint in the bean declaration.

Second question: I guess I should have only one proxied bean and not two beans as we can see in the second case?

like image 827
Mykeul Avatar asked Jul 01 '19 09:07

Mykeul


People also ask

What is the purpose of @RefreshScope in using Cloud configuration client?

The @RefreshScope annotation is used to load the configuration properties value from the Config server. Now, add the config server URL in your application. properties file and provide your application name. Note − http://localhost:8888 config server should be run before starting the config client application.

How do I enable actuator refresh endpoint?

You can invoke the refresh Actuator endpoint by sending an empty HTTP POST to the client's refresh endpoint: http://localhost:8080/actuator/refresh . Then you can confirm it worked by visting the http://localhost:8080/message endpoint.

How do I override spring Cloud config properties?

Overriding the Values of Remote Properties If you want to allow your applications to override the remote properties with their own System properties or config files, the remote property source has to grant it permission by setting spring. cloud. config. allowOverride=true (it doesn't work to set this locally).

How do I reload the configuration properties without restarting the app?

web. exposure. include=refresh: this is actuator property which will help us to refresh the properties without restarting the server.


1 Answers

Your understanding is a bit off and all of that is stated in the documentation.

From the javadoc of @RefreshScope.

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

So you will get 2 instances of the bean. 1 Proxy which will actually wrap an full instance of the bean. When being refreshed, the proxy will survice and the actual instance will be replaced.

From the 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).

So while it might be technically possible using references to those beans might not refresh, unless those are also marked as @RefreshScope.

In short the solution is to explicitly mark which beans need to be in @RefreshScope by either annotating the class as @RefreshScope or the @Bean method.

like image 176
M. Deinum Avatar answered Sep 21 '22 17:09

M. Deinum