-
Notifications
You must be signed in to change notification settings - Fork 346
NPE during AOT generation when @ConfigurationProperties contains generic property #1396
Description
With a dependency on consul-api and a @ConfigurationProperties bean that contains a ConsulClient field, AOT generation fails with a NullPointerException. While this particular use-case may be strange (having a ConsulClient field within @ConfigurationProperties), there is a more general case that reproduces this issue.
The dependency to add (Gradle):
implementation 'com.ecwid.consul:consul-api:1.4.5'
The properties class:
@ConfigurationProperties(prefix = "example")
public class DemoProperties {
private ConsulClient consulClient;
public void setConsulClient(ConsulClient consulClient) {
this.consulClient = consulClient;
}
public ConsulClient getConsulClient() {
return consulClient;
}
}
Enable it with @EnableConfigurationProperties(DemoProperties.class) and you get:
java.lang.NullPointerException
at org.springframework.boot.context.properties.ConfigurationPropertiesNativeConfigurationProcessor$TypeProcessor.handleJavaBeanProperties(ConfigurationPropertiesNativeConfigurationProcessor.java:156)
at org.springframework.boot.context.properties.ConfigurationPropertiesNativeConfigurationProcessor$TypeProcessor.process(ConfigurationPropertiesNativeConfigurationProcessor.java:112)
at org.springframework.boot.context.properties.ConfigurationPropertiesNativeConfigurationProcessor$TypeProcessor.process(ConfigurationPropertiesNativeConfigurationProcessor.java:94)
at org.springframework.boot.context.properties.ConfigurationPropertiesNativeConfigurationProcessor$TypeProcessor.handleJavaBeanProperties(ConfigurationPropertiesNativeConfigurationProcessor.java:159)
at org.springframework.boot.context.properties.ConfigurationPropertiesNativeConfigurationProcessor$TypeProcessor.process(ConfigurationPropertiesNativeConfigurationProcessor.java:112)
at org.springframework.boot.context.properties.ConfigurationPropertiesNativeConfigurationProcessor$TypeProcessor.process(ConfigurationPropertiesNativeConfigurationProcessor.java:94)
at org.springframework.boot.context.properties.ConfigurationPropertiesNativeConfigurationProcessor$TypeProcessor.handleJavaBeanProperties(ConfigurationPropertiesNativeConfigurationProcessor.java:159)
at org.springframework.boot.context.properties.ConfigurationPropertiesNativeConfigurationProcessor$TypeProcessor.process(ConfigurationPropertiesNativeConfigurationProcessor.java:112)
at org.springframework.boot.context.properties.ConfigurationPropertiesNativeConfigurationProcessor$TypeProcessor.process(ConfigurationPropertiesNativeConfigurationProcessor.java:94)
at org.springframework.boot.context.properties.ConfigurationPropertiesNativeConfigurationProcessor.processConfigurationProperties(ConfigurationPropertiesNativeConfigurationProcessor.java:70)
at org.springframework.boot.context.properties.ConfigurationPropertiesNativeConfigurationProcessor.process(ConfigurationPropertiesNativeConfigurationProcessor.java:64)
at org.springframework.aot.context.bootstrap.generator.infrastructure.nativex.NativeConfigurationRegistrar.lambda$processBeanFactory$0(NativeConfigurationRegistrar.java:55)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
at org.springframework.aot.context.bootstrap.generator.infrastructure.nativex.NativeConfigurationRegistrar.processBeanFactory(NativeConfigurationRegistrar.java:55)
at org.springframework.aot.context.bootstrap.generator.ApplicationContextAotProcessor.bootstrapMethod(ApplicationContextAotProcessor.java:107)
at org.springframework.aot.context.bootstrap.generator.ApplicationContextAotProcessor.process(ApplicationContextAotProcessor.java:95)
at org.springframework.aot.build.ContextBootstrapContributor.contribute(ContextBootstrapContributor.java:80)
at org.springframework.aot.build.BootstrapCodeGenerator.generate(BootstrapCodeGenerator.java:91)
at org.springframework.aot.build.BootstrapCodeGenerator.generate(BootstrapCodeGenerator.java:71)
at org.springframework.aot.build.GenerateBootstrapCommand.call(GenerateBootstrapCommand.java:107)
at org.springframework.aot.build.GenerateBootstrapCommand.call(GenerateBootstrapCommand.java:42)
at picocli.CommandLine.executeUserObject(CommandLine.java:1953)
at picocli.CommandLine.access$1300(CommandLine.java:145)
at picocli.CommandLine$RunLast.executeUserObjectOfLastSubcommandWithSameParent(CommandLine.java:2352)
at picocli.CommandLine$RunLast.handle(CommandLine.java:2346)
at picocli.CommandLine$RunLast.handle(CommandLine.java:2311)
at picocli.CommandLine$AbstractParseResultHandler.execute(CommandLine.java:2179)
at picocli.CommandLine.execute(CommandLine.java:2078)
at org.springframework.aot.build.GenerateBootstrapCommand.main(GenerateBootstrapCommand.java:112)
Debugging shows it's coming from the com.ecwid.consul.v1.Response class which has a generic field and a getter that is returning type T, causing its property type to be ? which then resolves to null:
public final class Response<T> {
private final T value;
public T getValue() {
return value;
}
}
This leads to the general case, where the NPE can be reproduced with any generic object as a field in a @ConfigurationProperties bean:
@ConfigurationProperties(prefix = "example")
public class DemoProperties {
private GenericObject<?> generic;
public GenericObject<?> getGeneric() {
return generic;
}
public static final class GenericObject<T> {
private final T value;
public GenericObject(T value) {
this.value = value;
}
public T getValue() {
return value;
}
}
}
Environment:
- Java 11
- Spring Boot 2.6.1
- Spring Native / Spring AOT 0.11.0