Upon context refresh for @ConfigurationProperties using @ConstructorBinding for immutability, the values contained within fails to be refreshed.

A sample project demonstrating the behaviour is provided at https://github.com/Kiongku/spring-remote-config which is written in Kotlin.

The Spring versions used are - Spring Boot: 2.2.4.RELEASE - Spring Cloud: Hoxton.SR1

Comment From: spencergibb

makes sense to me. it's immutable.

Comment From: Kiongku

Probably for more of a context, I was trying to see if immutable @ConfigurationProperties can be refreshed similarly as their mutable counterparts during a context refresh.

In my mind, it would be replacing the immutable @ConfigurationProperties with a new version with updated values which will be re-injected downstream. I did try to add @RefreshScope before on the REST controller but it had no effect.

I think the current mechanism just rebinds the @ConfigurationProperties which is fine for the old mutable version, but does not work well with the new @ConstructorBinding with final fields and no setters.

I would like to keep the Immutability of my ConfigurationProperties when using them and only allow it to be modified upon Spring Context refresh.

Comment From: spencergibb

I'm not sure rebinding will ever work on immutable config props. refresh scope on the controller doesn't make sense, maybe try it on the immutable config props bean?

Comment From: Kiongku

I was thinking on how to annotate the immutable config with @RefreshScope but @ConstructorBinding cannot be created as a @Bean or @Component. It can only be done through @EnableConfigurationProperties or @ConfigurationPropertiesScan.

Comment From: spencergibb

Thanks for the feedback, but we want to stick to the fact that immutable config doesn't change.

Comment From: Kiongku

Thanks for your time in reviewing this.

Comment From: colin-mullikin

@spencergibb I'm of the opinion that this should be looked at. Just because properties are immutable as far as Java is concerned (i.e. @ConstructorBinding is used, and no setters are present), that doesn't mean those properties should be completely immutable from outside configuration changes.

Also, if it is the case that those properties won't be refreshed, RefreshEventListener probably shouldn't log that keys have changed, and the meta/env endpoint probably should not return the new values (that aren't actually being used).

Comment From: spencergibb

Just because properties are immutable as far as Java is concerned (i.e. @ConstructorBinding is used, and no setters are present), that doesn't mean those properties should be completely immutable from outside configuration changes.

That is contradictory to me. Refresh scope is the way spring cloud has to support recreating a bean on refresh. You can use properties to add classes without using the annotation.

Also, if it is the case that those properties won't be refreshed, RefreshEventListener probably shouldn't log that keys have changed,

Just because a class that binds to those values in the Spring Environment doesn't mean they didn't change in the environment.

Comment From: colin-mullikin

If annotating the @ConfigurationProperties class with @RefreshScope updates the underlying values, then I retract my previous statement.

EDIT: The above does not work.

Comment From: spencergibb

It was noted above that @RefreshScope doesn't work already. Did you try with spring.cloud.refresh.extra-refreshable=?

Comment From: colin-mullikin

My application no longer starts if I add that to my bootstrap.yml.

Error message: "@EnableConfigurationProperties or @ConfigurationPropertiesScan must be used to add @ConstructorBinding type"

@ConfigurationPropertiesScan is already present and works when I don't have the extra-refreshable property.

Comment From: spencergibb

why can't you use mutable config classes?

Comment From: colin-mullikin

I could (and always have in the past, as it was the only option), but immutable would be preferred, as there is no reason for the application code to ever update the configuration. I thought using @ConstructorBinding would provide the functionality I desired, but I guess I was mistaken.

Comment From: spencergibb

:man_shrugging: I'm going to keep this closed and use immutable configuration as a marker that it shouldn't be refreshed. If you want it to be, use mutable configuration objects.

Comment From: reda-alaoui

Hi,

I'm also of the opinion this should be looked at.

While I want the framework to refresh/reload a @ConfigurationProperties , I don't want the application to change its value at runtime. We use immutable @ConfigurationProperties (with @ConstructorBinding) to prevent app developers from mutating the global application state at runtime. It is especially tempting for a junior developer to mutate the configuration in the test context.

Because of these constraints and the current state of Spring Cloud, we only have the following alternatives: - use Environment.getProperty() methods - use Binder with @ConstructorBinding classes. This is verbose and prevents Spring Boot Annotation processor usage.

Comment From: spencergibb

I'm afraid protecting jr developers during testing is not a strong enough argument for what is likely a non trivial feature to support.