Autowiring configuration isn't generated for beans provided by interface, leading to uninitialized values at runtime:
Caused by: java.lang.NullPointerException: null
at java.base/java.util.Objects.requireNonNull(Objects.java:209) ~[na:na]
at com.example.demo.DemoApplication.s3Client(DemoApplication.java:26) ~[classes!/:0.0.1-SNAPSHOT]
at com.example.demo.DemoApplication__BeanDefinitions.lambda$getSClientInstanceSupplier$1(DemoApplication__BeanDefinitions.java:49) ~[classes!/:0.0.1-SNAPSHOT]
If the bean definition is changed to use the concrete implementation, the Autowiring
class is generated and the values resolved:
/**
* Autowiring for {@link DemoApplication.S3ClientConfigurationImpl}.
*/
public class DemoApplication_S3ClientConfigurationImpl__Autowiring {
/**
* Apply the autowiring.
*/
public static DemoApplication.S3ClientConfigurationImpl apply(RegisteredBean registeredBean,
DemoApplication.S3ClientConfigurationImpl instance) {
AutowiredFieldValueResolver.forRequiredField("bucket").resolveAndSet(registeredBean, instance);
AutowiredFieldValueResolver.forRequiredField("ec2Region").resolveAndSet(registeredBean, instance);
return instance;
}
}
Example project:
https://github.com/DanielThomas/spring-aot-issues/tree/dannyt/autowired-values
Run and note the failure:
./gradlew bootJar && java -Dspring.aot.enabled=true -jar build/libs/demo-0.0.1-SNAPSHOT.jar
Comment From: snicoll
I am afraid this is to be expected. If the interface is returned, then AOT considers that to be the bean type. If a sub-class is actually instantiated, we can't detect that as we usually do at runtime. If the context needs to process anything on the sub-class, such as lifecycle callbacks or autowired members, then the sub-class must be exposed.
There is a section in the reference documentation about this, see https://docs.spring.io/spring-framework/reference/core/aot.html#aot.bestpractices.bean-type