Skip to content

Unexpected constructor-based initialization of @ConfigurationProperties leads to inconsistent behavior #16038

@axtavt

Description

@axtavt

Consider the following test case:

    @RunWith(SpringRunner.class)
    @SpringBootTest(classes = { BindingTest.Config.class })
    @EnableConfigurationProperties
    @TestPropertySource
    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 @Getter @Setter
            private String bar;
        
            public FooProperties() {}
        
            public FooProperties(String bar) { // (1)
                 this.bar = bar;
            }
        }
    }

BindingTest.properties:

foo.bar=baz
foo=unrelated # (2)

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 is null
  • no validation error is thrown

This happens because Binder decides to use ObjectToObjectConverter to initialize FooProperties using its single argument constructor (despite the fact that FooProperties is explicitly created in @Bean method), creates new instance of FooProperties and applies validation to it, but ConfigurationPropertiesBindingPostProcessor ignores that new instance.

This applies to Spring Boot 2.0.x, 2.1.x, 2.2.x.

Metadata

Metadata

Labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions