I have a record NestedProperties which is a member of a Configuration as follows. I want to directly inject NestedProperties and hence I am trying to declare it as a bean.
@ConfigurationProperties("app")
public record AppProperties(NestedProperties nested) {
@Bean
@Override
@ConfigurationProperties("nested")
public NestedProperties nested() {
return nested;
}
}
Launching the Spring Boot application results in the following exception.
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'nested' defined in com.example.demo.AppProperties: Initialization of bean failed; nested exception is java.lang.IllegalStateException: Cannot bind @ConfigurationProperties for bean 'nested'. Ensure that @ConstructorBinding has not been applied to regular bean
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:628) ~[spring-beans-5.3.22.jar:5.3.22]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542) ~[spring-beans-5.3.22.jar:5.3.22]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[spring-beans-5.3.22.jar:5.3.22]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.22.jar:5.3.22]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.22.jar:5.3.22]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.22.jar:5.3.22]
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276) ~[spring-beans-5.3.22.jar:5.3.22]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1391) ~[spring-beans-5.3.22.jar:5.3.22]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1311) ~[spring-beans-5.3.22.jar:5.3.22]
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:887) ~[spring-beans-5.3.22.jar:5.3.22]
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791) ~[spring-beans-5.3.22.jar:5.3.22]
... 19 common frames omitted
Caused by: java.lang.IllegalStateException: Cannot bind @ConfigurationProperties for bean 'nested'. Ensure that @ConstructorBinding has not been applied to regular bean
at org.springframework.util.Assert.state(Assert.java:76) ~[spring-core-5.3.22.jar:5.3.22]
at org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor.bind(ConfigurationPropertiesBindingPostProcessor.java:86) ~[spring-boot-2.7.2.jar:2.7.2]
at org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor.postProcessBeforeInitialization(ConfigurationPropertiesBindingPostProcessor.java:78) ~[spring-boot-2.7.2.jar:2.7.2]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:440) ~[spring-beans-5.3.22.jar:5.3.22]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1796) ~[spring-beans-5.3.22.jar:5.3.22]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:620) ~[spring-beans-5.3.22.jar:5.3.22]
... 29 common frames omitted
Minimal Reproducible Example
https://github.com/naiyerasif/configuration-properties-issue/commit/47defdbcd0a019b359556d57be866d485872fb19
Additional details
If I convert the NestedProperties to regular class, things work as expected.
Version details
This is happening with Spring Boot 2.7.2 and Java 17.
Comment From: snicoll
@naiyerasif what is this @Bean on your "nested" record? A @ConfigurationProperties-annotated type is not a @Configuration.
Comment From: naiyerasif
@snicoll The @Bean is @org.springframework.context.annotation.Bean (see reproducible example). I am trying to initialize a NestedProperties bean so that instead of injecting AppProperties and calling appProperties.nested(), I can simply inject NestedProperties directly wherever I need it.
I am trying to initialize a NestedProperties bean so that instead of injecting AppProperties and calling appProperties.nested(), I can simply inject NestedProperties directly wherever I need it.
It works as expected if I declare NestedProperties as a regular Java class as follows but throws the exception (pasted in the issue description above) when it is declared as a record.
public class NestedProperties {
private final String ext;
public NestedProperties(String ext) {
this.ext = ext;
}
public String ext() {
return ext;
}
}
Comment From: snicoll
Yes, I am aware of what @Bean is. Nested configuration properties are not meant to be injected, they are part of the main object that defines it. I've pushed a fix to your sample project to show you how it's supposed to be used: https://github.com/naiyerasif/configuration-properties-issue/pull/1
Comment From: naiyerasif
@snicoll I am dealing with a usecase where I've a lot of deeply nested properties which are directly injected as beans throughout the codebase. This is actually a discrepancy in existing behavior since the nested properties declared in a class work properly as beans while the same properties declared in a record don't.
Nested configuration properties are not meant to be injected.
By this comment, I believe the above behavior was a hack. Thanks for this clarification.