Consistent configuration property binding for nested classes on JVM and native-image
See original GitHub issueWhile working on the AOT smoke test configuration-properties I encountered a case where binding a nested class fails in native image. This works in JVM mode.
See the failing build here: https://ci.spring.io/teams/spring-aot-smoke-tests/pipelines/spring-aot-smoke-tests-1.0.x/jobs/configuration-properties
2022-07-13T15:35:51.578+02:00 WARN 44862 --- [ main] o.s.c.support.GenericApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'CLR': Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'app-com.example.configprops.AppProperties': Could not bind properties to 'AppProperties' : prefix=app, ignoreInvalidFields=false, ignoreUnknownFields=true
2022-07-13T15:35:51.579+02:00 DEBUG 44862 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter : Application failed to start due to an exception
org.springframework.boot.context.properties.bind.BindException: Failed to bind properties under 'app.nested-list' to java.util.List<com.example.configprops.AppProperties$Nested>
at org.springframework.boot.context.properties.bind.Binder.handleBindError(Binder.java:387) ~[configuration-properties:3.0.0-SNAPSHOT]
at org.springframework.boot.context.properties.bind.Binder.bind(Binder.java:347) ~[configuration-properties:3.0.0-SNAPSHOT]
at org.springframework.boot.context.properties.bind.Binder.lambda$bindDataObject$4(Binder.java:472) ~[configuration-properties:3.0.0-SNAPSHOT]
at org.springframework.boot.context.properties.bind.JavaBeanBinder.bind(JavaBeanBinder.java:95) ~[na:na]
at org.springframework.boot.context.properties.bind.JavaBeanBinder.bind(JavaBeanBinder.java:83) ~[na:na]
at org.springframework.boot.context.properties.bind.JavaBeanBinder.bind(JavaBeanBinder.java:59) ~[na:na]
at org.springframework.boot.context.properties.bind.Binder.lambda$bindDataObject$5(Binder.java:476) ~[configuration-properties:3.0.0-SNAPSHOT]
at org.springframework.boot.context.properties.bind.Binder$Context.withIncreasedDepth(Binder.java:590) ~[na:na]
at org.springframework.boot.context.properties.bind.Binder$Context.withDataObject(Binder.java:576) ~[na:na]
at org.springframework.boot.context.properties.bind.Binder.bindDataObject(Binder.java:474) ~[configuration-properties:3.0.0-SNAPSHOT]
at org.springframework.boot.context.properties.bind.Binder.bindObject(Binder.java:414) ~[configuration-properties:3.0.0-SNAPSHOT]
at org.springframework.boot.context.properties.bind.Binder.bind(Binder.java:343) ~[configuration-properties:3.0.0-SNAPSHOT]
at org.springframework.boot.context.properties.bind.Binder.bind(Binder.java:332) ~[configuration-properties:3.0.0-SNAPSHOT]
at org.springframework.boot.context.properties.bind.Binder.bind(Binder.java:262) ~[configuration-properties:3.0.0-SNAPSHOT]
at org.springframework.boot.context.properties.bind.Binder.bind(Binder.java:249) ~[configuration-properties:3.0.0-SNAPSHOT]
at org.springframework.boot.context.properties.ConfigurationPropertiesBinder.bind(ConfigurationPropertiesBinder.java:95) ~[configuration-properties:3.0.0-SNAPSHOT]
at org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor.bind(ConfigurationPropertiesBindingPostProcessor.java:89) ~[configuration-properties:3.0.0-SNAPSHOT]
at org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor.postProcessBeforeInitialization(ConfigurationPropertiesBindingPostProcessor.java:78) ~[configuration-properties:3.0.0-SNAPSHOT]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:425) ~[configuration-properties:6.0.0-SNAPSHOT]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1745) ~[configuration-properties:6.0.0-SNAPSHOT]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:604) ~[configuration-properties:6.0.0-SNAPSHOT]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:526) ~[configuration-properties:6.0.0-SNAPSHOT]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326) ~[configuration-properties:6.0.0-SNAPSHOT]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[configuration-properties:6.0.0-SNAPSHOT]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324) ~[configuration-properties:6.0.0-SNAPSHOT]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[configuration-properties:6.0.0-SNAPSHOT]
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) ~[configuration-properties:6.0.0-SNAPSHOT]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1374) ~[configuration-properties:6.0.0-SNAPSHOT]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1294) ~[configuration-properties:6.0.0-SNAPSHOT]
at org.springframework.beans.factory.aot.AutowiredInstantiationArgumentsResolver.resolveArgument(AutowiredInstantiationArgumentsResolver.java:302) ~[na:na]
at org.springframework.beans.factory.aot.AutowiredInstantiationArgumentsResolver.resolveArguments(AutowiredInstantiationArgumentsResolver.java:232) ~[na:na]
at org.springframework.beans.factory.aot.AutowiredInstantiationArgumentsResolver.resolve(AutowiredInstantiationArgumentsResolver.java:154) ~[na:na]
at com.example.configprops.CLR__BeanDefinitions.getCLRInstance(CLR__BeanDefinitions.java:30) ~[na:na]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainInstanceFromSupplier(AbstractAutowireCapableBeanFactory.java:1224) ~[configuration-properties:6.0.0-SNAPSHOT]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainFromSupplier(AbstractAutowireCapableBeanFactory.java:1209) ~[configuration-properties:6.0.0-SNAPSHOT]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1156) ~[configuration-properties:6.0.0-SNAPSHOT]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:566) ~[configuration-properties:6.0.0-SNAPSHOT]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:526) ~[configuration-properties:6.0.0-SNAPSHOT]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326) ~[configuration-properties:6.0.0-SNAPSHOT]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[configuration-properties:6.0.0-SNAPSHOT]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324) ~[configuration-properties:6.0.0-SNAPSHOT]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[configuration-properties:6.0.0-SNAPSHOT]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:930) ~[configuration-properties:6.0.0-SNAPSHOT]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:926) ~[configuration-properties:6.0.0-SNAPSHOT]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:592) ~[configuration-properties:6.0.0-SNAPSHOT]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:729) ~[configuration-properties:3.0.0-SNAPSHOT]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:428) ~[configuration-properties:3.0.0-SNAPSHOT]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:310) ~[configuration-properties:3.0.0-SNAPSHOT]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1301) ~[configuration-properties:3.0.0-SNAPSHOT]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1290) ~[configuration-properties:3.0.0-SNAPSHOT]
at com.example.configprops.ConfigPropsApplication.main(ConfigPropsApplication.java:10) ~[configuration-properties:na]
Caused by: org.springframework.boot.context.properties.bind.UnboundConfigurationPropertiesException: The elements [app.nested-list[0].a-int,app.nested-list[1].a-int] were left unbound.
at org.springframework.boot.context.properties.bind.IndexedElementsBinder.assertNoUnboundChildren(IndexedElementsBinder.java:136) ~[configuration-properties:3.0.0-SNAPSHOT]
at org.springframework.boot.context.properties.bind.IndexedElementsBinder.bindIndexed(IndexedElementsBinder.java:113) ~[configuration-properties:3.0.0-SNAPSHOT]
at org.springframework.boot.context.properties.bind.IndexedElementsBinder.bindIndexed(IndexedElementsBinder.java:86) ~[configuration-properties:3.0.0-SNAPSHOT]
at org.springframework.boot.context.properties.bind.IndexedElementsBinder.bindIndexed(IndexedElementsBinder.java:70) ~[configuration-properties:3.0.0-SNAPSHOT]
at org.springframework.boot.context.properties.bind.CollectionBinder.bindAggregate(CollectionBinder.java:49) ~[na:na]
at org.springframework.boot.context.properties.bind.AggregateBinder.bind(AggregateBinder.java:56) ~[configuration-properties:3.0.0-SNAPSHOT]
at org.springframework.boot.context.properties.bind.Binder.lambda$bindAggregate$3(Binder.java:438) ~[configuration-properties:3.0.0-SNAPSHOT]
at org.springframework.boot.context.properties.bind.Binder$Context.withIncreasedDepth(Binder.java:590) ~[na:na]
at org.springframework.boot.context.properties.bind.Binder.bindAggregate(Binder.java:438) ~[configuration-properties:3.0.0-SNAPSHOT]
at org.springframework.boot.context.properties.bind.Binder.bindObject(Binder.java:399) ~[configuration-properties:3.0.0-SNAPSHOT]
at org.springframework.boot.context.properties.bind.Binder.bind(Binder.java:343) ~[configuration-properties:3.0.0-SNAPSHOT]
... 49 common frames omitted
2022-07-13T15:35:51.579+02:00 ERROR 44862 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter :
***************************
APPLICATION FAILED TO START
***************************
Description:
Binding to target [Bindable@270c5b7b type = java.util.List<com.example.configprops.AppProperties$Nested>, value = 'provided', annotations = array<Annotation>[[empty]]] failed:
Property: app.nested-list[0].a-int
Value: 1
Origin: class path resource [application.yaml] - 6:14
Reason: The elements [app.nested-list[0].a-int,app.nested-list[1].a-int] were left unbound.
Property: app.nested-list[1].a-int
Value: 2
Origin: class path resource [application.yaml] - 7:14
Reason: The elements [app.nested-list[0].a-int,app.nested-list[1].a-int] were left unbound.
Action:
Update your application's configuration
Issue Analytics
- State:
- Created a year ago
- Comments:10 (10 by maintainers)
Top Results From Across the Web
spring boot binding app properties for nested class
Generally, when we load the properties, they are considered CONSTANT unless the application is restarted. In that case, the properties can ...
Read more >Core Features - Spring
If nested POJO properties are initialized (like the Security field in the ... To use constructor binding in a native image the class...
Read more >All configuration options - Quarkus
AWS Lambda Type Default
AWS Lambda Common Type Default
AWS Lambda Gateway REST API Type Default
Agroal ‑ Database connection pool Type Default
Read more >Reflection on Native Image - GraalVM - Oracle Help Center
Java reflection support (the java.lang.reflect.* API) enables Java code to examine its own classes, methods, fields and their properties at run time. Native...
Read more >Jersey 2.37 User Guide - GitHub Pages
Java API for JSON Binding (JSON-B) ... Apache 5 HTTP client configuration properties; A.7. ... Mapping of Jersey 1.x to JAX-RS 2.0 client...
Read more >
Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found

We now generate hints correctly for nested generics.
This feels like something we overlooked in #30916. As it has already shipped, I think we should handle this as a bug as it’s something that we expected to work now but does not.