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