Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ConfigurationPropertiesRebinder not compatible with @ConstructorBinding when constructed in bootstrap #844

Open
symposion opened this issue Nov 3, 2020 · 7 comments
Assignees

Comments

@symposion
Copy link

symposion commented Nov 3, 2020

Using Hoxton.SR8

If you define an @ConfigurationProperties class that uses @ConstructorBinding, attempts to rebind it using ConfigurationPropertiesRebinder will fail with an exception from ConfigurationPropertiesBindingPostProcessor: ""Cannot bind @ConfigurationProperties for bean XXXX Ensure that @ConstructorBinding has not been applied to regular bean"

This is especially problematic because Spring Cloud Context rebinds all ConfigurationProperties classes that have been used in the bootstrap context in order to ensure that property changes during the bootstrap phase are picked up. Looking at the code, it's not clear to me that there's a simple fix for this: the rebinder fundamentally assumes that the properties bean is mutable. In order to support immutable properties classes I guess it would need some sort of proxy in the middle to allow the properties bean to be re-constructed without having to reinject it into every dependent bean.

@spencergibb
Copy link
Member

We are not going to support rebinding @ConstructorBinding properties.

@spencergibb
Copy link
Member

I'm unable to reproduce the error log. When I do it, it value doesn't update and no exception is logged. Can you provide a complete, minimal, verifiable sample that reproduces the problem? It should be available as a GitHub (or similar) project or attached to this issue as a zip file.

@symposion
Copy link
Author

https://github.com/symposion/spring-test (The master branch)

I've created an immutable @ConfigurationProperties class that I have no intention of mutating, I'm not explicitly refreshsing /rebinding anything, and the app fails to startup because internally Spring cloud is rebinding all the properties from the bootstrap context. I understand why that's done, but at the very least this is surprising and worth calling out in the docs as a potential "gotcha". A nicer alternative might be to just ignore properties beans that are constructor-bound when rebinding with a warning in the logs rather than a hard fail; or make the post-bootstrap rebinding optional somehow?

@spencergibb spencergibb self-assigned this Mar 30, 2021
@spencergibb spencergibb added this to the 2.2.8.RELEASE milestone Mar 30, 2021
@spencergibb spencergibb removed the bug label Mar 30, 2021
@spencergibb spencergibb removed this from the 2.2.8.RELEASE milestone Mar 30, 2021
@spencergibb spencergibb changed the title ConfigurationPropertiesRebinder not compatible with @ConstructorBinding ConfigurationPropertiesRebinder not compatible with @ConstructorBinding when constructed in bootstrap Mar 30, 2021
@spencergibb
Copy link
Member

This only applies to constructor binding beans that are created in a bootstrap configuration. Considering this is relatively uncommon and I"m not sure how to distinguish if it was created in bootstrap or not, I'm going to mark this as waiting for votes.

@candrews
Copy link

FWIW, I just ran into this problem

@efenderbosch
Copy link

efenderbosch commented Jun 28, 2021

Pretty sure I encountered this as well. I'm trying to create something like spring-cloud-aws-parameter-store-config that uses the v2 AWS SDK and has different rules for the parameter store path. My class is simply:

@ConstructorBinding
@ConfigurationProperties(AwsParamStoreProperties.CONFIG_PREFIX)
data class AwsParamStoreProperties(
    val region: String? = null,
    val endpoint: URI? = null,
    val name: String? = null,
    val enabled: Boolean = true
) {
    companion object {
        const val CONFIG_PREFIX = "aws.paramstore"
    }
}

It loads the parameters from SSM, but when the application context tries to import the ConfigData from the bootstrap context, it throws the same exception. It does work when defined like this:

@ConfigurationProperties(AwsParamStoreProperties.CONFIG_PREFIX)
class AwsParamStoreProperties {
    var region: String? = null
    var endpoint: URI? = null
    var name: String? = null
    var enabled: Boolean = true

    companion object {
        const val CONFIG_PREFIX = "aws.paramstore"
    }
}

But then we lose immutability.

Using Spring Boot 2.5.1, spring-cloud-context 3.0.3.

@sjoerd222888
Copy link

Are there any other workarounds?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants