-
Notifications
You must be signed in to change notification settings - Fork 41.2k
Unexpected constructor-based initialization of @ConfigurationProperties leads to inconsistent behavior #16038
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
Comments
Thanks for the test case. For our future reference, here's a slightly modified version that is standalone and doesn't rely on Lombok: package com.example.demo;
import static org.assertj.core.api.Assertions.assertThat;
import javax.validation.constraints.NotNull;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.validation.annotation.Validated;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = { BindingTest.Config.class })
@EnableConfigurationProperties
@TestPropertySource(properties = {"foo.bar=baz", "foo=unrelated"})
public class BindingTest {
@Autowired
FooProperties fooProperties;
@Test
public void shouldInjectBar() {
assertThat(fooProperties.getBar()).isEqualTo("baz");
}
@Configuration
public static class Config {
@Bean
public FooProperties fooProperties() {
return new FooProperties();
}
}
@ConfigurationProperties("foo")
@Validated
public static class FooProperties {
@NotNull
private String bar;
public FooProperties() {}
public FooProperties(String bar) { // (1)
this.bar = bar;
}
public String getBar() {
return bar;
}
public void setBar(String bar) {
this.bar = bar;
}
}
} A few comments about the example: It's unusual for a Having both foo: unrelated
bar: baz We've made this mistake ourselves in the past and intend to correct it in 2.2. See #12510 for some details. With that said, I think it makes sense for the binder to ignore a property named |
@wilkinsona I want to work on it. Please let me know if I can start working on it ? |
@Raheela1024 thank you for the offer. Please go ahead and let us know if you need assistance. |
@Raheela1024 How's it going? Let us know if there is anything we can help with. |
@mbhave Sorry i was busy but now looking into will update shortly. |
@mbhave I need some help regarding the fix should we need to revert
or we need to fix into |
@Raheela1024 I'd like to help but I don't see the link. We should not revert the change, it's part of an ongoing effort that a property name should not match the name of a group. Can you please expand a bit of what the problem is? You can share what you have on your fork. |
@snicoll thanks for your reply. I have debugged the issue by changing test case into |
@Raheela1024 As @wilkinsona mentioned here, we want to ignore any property that matches the prefix of the |
We probably need to consider doing something so that this change only affects |
I've got a fix for this, but it includes API changes so I'm only going to apply it to 2.5 |
Reopening to consider nested properties |
Example for nested properties public class JavaBeanWithPublicConstructor {
private String value;
private Bar bar = new Bar();
public JavaBeanWithPublicConstructor() {
}
public JavaBeanWithPublicConstructor(String value) {
setValue(value);
}
public String getValue() {
return this.value;
}
public void setValue(String value) {
this.value = value;
}
public Bar getBar() {
return bar;
}
public void setBar(Bar bar) {
this.bar = bar;
}
static class Bar {
private String baz;
public Bar() {
}
public Bar(String baz) {
this.baz = baz;
}
public String getBaz() {
return baz;
}
public void setBaz(String baz) {
this.baz = baz;
}
}
} |
We'll look at nested ones in #25368 |
Uh oh!
There was an error while loading. Please reload this page.
Consider the following test case:
BindingTest.properties
:I expected
fooProperties.bar
to be populated, or, at least,@NotNull
validation error to be thrown.However, in presence of single argument constructor (1) and conflicting property (2) (it may be, for example, a totally unrelated system environment variable), I got the following:
fooProperties.bar
isnull
This happens because
Binder
decides to useObjectToObjectConverter
to initializeFooProperties
using its single argument constructor (despite the fact thatFooProperties
is explicitly created in@Bean
method), creates new instance ofFooProperties
and applies validation to it, butConfigurationPropertiesBindingPostProcessor
ignores that new instance.This applies to Spring Boot 2.0.x, 2.1.x, 2.2.x.
The text was updated successfully, but these errors were encountered: