The constructor binding feature is very cool. But there are cases that does not work fine when integrating 3rd party configuration class and it deploy to the application server as war file(= When JndiPropertySource is enabled).
For example, following properties class does not work.
@ConfigurationProperties(prefix = "my")
public class MyProperties {
private String name;
@NestedConfigurationProperty
private ThirdPartyConfiguration configuration; // Third party class
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setConfiguration(ThirdPartyConfiguration configuration) {
this.configuration = configuration;
}
public ThirdPartyConfiguration getConfiguration() {
return configuration;
}
}
public class ThirdPartyConfiguration {
private String encoding;
private boolean enabled;
private final List<Attribute> attributes = new ArrayList<>();
public void setEncoding(String encoding) {
this.encoding = encoding;
}
public String getEncoding() {
return encoding;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public boolean isEnabled() {
return enabled;
}
public void addAttribute(Attribute attribute) {
this.attributes.add(attribute);
}
public List<Attribute> getAttributes() {
return attributes;
}
public static class Attribute {
private final String name;
private final Object value;
public Attribute(String name, Object value) {
Objects.requireNonNull(name, "'name' must be not null.");
Objects.requireNonNull(value, "'value' must be not null.");
this.name = name;
this.value = value;
}
public String getName() {
return name;
}
public Object getValue() {
return value;
}
}
}
# empty
...
Caused by: org.springframework.boot.context.properties.bind.BindException: Failed to bind properties under 'my.configuration.attributes[0]' to com.example.springbootstartergh350.ThirdPartyConfiguration$Attribute
at org.springframework.boot.context.properties.bind.Binder.handleBindError(Binder.java:243)
at org.springframework.boot.context.properties.bind.Binder.bind(Binder.java:219)
at org.springframework.boot.context.properties.bind.Binder.lambda$null$0(Binder.java:291)
at org.springframework.boot.context.properties.bind.Binder$Context.withSource(Binder.java:406)
at org.springframework.boot.context.properties.bind.Binder$Context.access$1000(Binder.java:373)
at org.springframework.boot.context.properties.bind.Binder.lambda$bindAggregate$1(Binder.java:292)
at org.springframework.boot.context.properties.bind.IndexedElementsBinder.bindIndexed(IndexedElementsBinder.java:106)
at org.springframework.boot.context.properties.bind.IndexedElementsBinder.bindIndexed(IndexedElementsBinder.java:86)
at org.springframework.boot.context.properties.bind.IndexedElementsBinder.bindIndexed(IndexedElementsBinder.java:71)
at org.springframework.boot.context.properties.bind.CollectionBinder.bindAggregate(CollectionBinder.java:49)
at org.springframework.boot.context.properties.bind.AggregateBinder.bind(AggregateBinder.java:56)
at org.springframework.boot.context.properties.bind.Binder.lambda$bindAggregate$2(Binder.java:294)
at org.springframework.boot.context.properties.bind.Binder$Context.withIncreasedDepth(Binder.java:430)
at org.springframework.boot.context.properties.bind.Binder$Context.access$200(Binder.java:373)
at org.springframework.boot.context.properties.bind.Binder.bindAggregate(Binder.java:294)
at org.springframework.boot.context.properties.bind.Binder.bindObject(Binder.java:255)
at org.springframework.boot.context.properties.bind.Binder.bind(Binder.java:215)
at org.springframework.boot.context.properties.bind.Binder.lambda$bindBean$3(Binder.java:327)
at org.springframework.boot.context.properties.bind.JavaBeanBinder.bind(JavaBeanBinder.java:80)
at org.springframework.boot.context.properties.bind.JavaBeanBinder.bind(JavaBeanBinder.java:70)
at org.springframework.boot.context.properties.bind.JavaBeanBinder.bind(JavaBeanBinder.java:54)
at org.springframework.boot.context.properties.bind.Binder.lambda$null$4(Binder.java:330)
at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
at java.util.ArrayList$ArrayListSpliterator.tryAdvance(ArrayList.java:1359)
at java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:126)
at java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:498)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:485)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
at java.util.stream.FindOps$FindOp.evaluateSequential(FindOps.java:152)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.findFirst(ReferencePipeline.java:464)
at org.springframework.boot.context.properties.bind.Binder.lambda$bindBean$5(Binder.java:331)
at org.springframework.boot.context.properties.bind.Binder$Context.withIncreasedDepth(Binder.java:430)
at org.springframework.boot.context.properties.bind.Binder$Context.withBean(Binder.java:416)
at org.springframework.boot.context.properties.bind.Binder$Context.access$500(Binder.java:373)
at org.springframework.boot.context.properties.bind.Binder.bindBean(Binder.java:329)
at org.springframework.boot.context.properties.bind.Binder.bindObject(Binder.java:270)
at org.springframework.boot.context.properties.bind.Binder.bind(Binder.java:215)
at org.springframework.boot.context.properties.bind.Binder.lambda$bindBean$3(Binder.java:327)
at org.springframework.boot.context.properties.bind.JavaBeanBinder.bind(JavaBeanBinder.java:80)
at org.springframework.boot.context.properties.bind.JavaBeanBinder.bind(JavaBeanBinder.java:70)
at org.springframework.boot.context.properties.bind.JavaBeanBinder.bind(JavaBeanBinder.java:54)
at org.springframework.boot.context.properties.bind.Binder.lambda$null$4(Binder.java:330)
at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
at java.util.ArrayList$ArrayListSpliterator.tryAdvance(ArrayList.java:1359)
at java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:126)
at java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:498)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:485)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
at java.util.stream.FindOps$FindOp.evaluateSequential(FindOps.java:152)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.findFirst(ReferencePipeline.java:464)
at org.springframework.boot.context.properties.bind.Binder.lambda$bindBean$5(Binder.java:331)
at org.springframework.boot.context.properties.bind.Binder$Context.withIncreasedDepth(Binder.java:430)
at org.springframework.boot.context.properties.bind.Binder$Context.withBean(Binder.java:416)
at org.springframework.boot.context.properties.bind.Binder$Context.access$500(Binder.java:373)
at org.springframework.boot.context.properties.bind.Binder.bindBean(Binder.java:329)
at org.springframework.boot.context.properties.bind.Binder.bindObject(Binder.java:270)
at org.springframework.boot.context.properties.bind.Binder.bind(Binder.java:215)
at org.springframework.boot.context.properties.bind.Binder.bind(Binder.java:203)
at org.springframework.boot.context.properties.bind.Binder.bind(Binder.java:186)
at org.springframework.boot.context.properties.ConfigurationPropertiesBinder.bind(ConfigurationPropertiesBinder.java:93)
at org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor.bind(ConfigurationPropertiesBindingPostProcessor.java:115)
... 66 more
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.example.springbootstartergh350.ThirdPartyConfiguration$Attribute]: Constructor threw exception; nested exception is java.lang.NullPointerException: 'name' is not null.
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:213)
at org.springframework.boot.context.properties.bind.ConstructorParametersBinder.bind(ConstructorParametersBinder.java:60)
at org.springframework.boot.context.properties.bind.Binder.lambda$null$4(Binder.java:330)
at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
at java.util.ArrayList$ArrayListSpliterator.tryAdvance(ArrayList.java:1359)
at java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:126)
at java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:498)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:485)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
at java.util.stream.FindOps$FindOp.evaluateSequential(FindOps.java:152)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.findFirst(ReferencePipeline.java:464)
at org.springframework.boot.context.properties.bind.Binder.lambda$bindBean$5(Binder.java:331)
at org.springframework.boot.context.properties.bind.Binder$Context.withIncreasedDepth(Binder.java:430)
[2019-06-11 03:48:48,181] Artifact spring-boot-starter-gh-350:war: Error during artifact deployment. See server log for details.
at org.springframework.boot.context.properties.bind.Binder$Context.withBean(Binder.java:416)
at org.springframework.boot.context.properties.bind.Binder$Context.access$500(Binder.java:373)
at org.springframework.boot.context.properties.bind.Binder.bindBean(Binder.java:329)
at org.springframework.boot.context.properties.bind.Binder.bindObject(Binder.java:270)
at org.springframework.boot.context.properties.bind.Binder.bind(Binder.java:215)
... 127 more
Caused by: java.lang.NullPointerException: 'name' must be not null.
at java.util.Objects.requireNonNull(Objects.java:228)
at com.example.springbootstartergh350.ThirdPartyConfiguration$Attribute.<init>(ThirdPartyConfiguration.java:47)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:200)
... 145 more
I've added repro project. This issue can reproduce using cargo plugin as follow:
$ ./mvnw package cargo:run
@philwebb Thanks for fix! I've confirmed to fix this issue on 2.2.0M4.
@kazuki43zoo Actually @mbhave fixed it, I just did the merge. Thanks for testing M4.