Skip to content

Constructor binding can fail with non iterable property sources #17098

@kazuki43zoo

Description

@kazuki43zoo

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).

Versions

  • 2.2.0.M3 and snapshot version
  • Tomcat 9.0.20

Details

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;
    }

  }

}
  • application.properties
# empty

Stacktrace

...
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

Repro project

spring-boot-gh-17098-master.zip

Metadata

Metadata

Labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions