Spring-Boot version 3.2.4

The processAot task does not generate the right BeanDefinitions files, if a configuration class uses composition of beans trough interfaces.

See showcase project

Comment From: snicoll

@MartinLei thanks for the sample but the README instructs to list a file without additional information. The app starts fine so I am not really sure what you mean.

Comment From: MartinLei

Thank @snicoll for your prompt feedback. Please excuse my very short README.md. I added more details how to reproduce the problem for spring native builds.

I hope this makes it more clear :)

Comment From: snicoll

This is working fine with AOT on the JVM. It does fail on native with:

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'demoApplication': Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'createACar': Instantiation of supplied bean failed
    at org.springframework.beans.factory.aot.BeanInstanceSupplier.resolveAutowiredArgument(BeanInstanceSupplier.java:344) ~[na:na]
    at org.springframework.beans.factory.aot.BeanInstanceSupplier.resolveArguments(BeanInstanceSupplier.java:264) ~[na:na]
    at org.springframework.beans.factory.aot.BeanInstanceSupplier.get(BeanInstanceSupplier.java:204) ~[na:na]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.obtainInstanceFromSupplier(DefaultListableBeanFactory.java:949) ~[demo:6.1.5]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainFromSupplier(AbstractAutowireCapableBeanFactory.java:1217) ~[demo:6.1.5]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1161) ~[demo:6.1.5]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:562) ~[demo:6.1.5]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522) ~[demo:6.1.5]
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326) ~[demo:6.1.5]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[demo:6.1.5]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324) ~[demo:6.1.5]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[demo:6.1.5]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:975) ~[demo:6.1.5]
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:962) ~[demo:6.1.5]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:624) ~[demo:6.1.5]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) ~[demo:3.2.4]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456) ~[demo:3.2.4]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:334) ~[demo:3.2.4]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1354) ~[demo:3.2.4]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1343) ~[demo:3.2.4]
    at com.example.demo.DemoApplication.main(DemoApplication.java:14) ~[demo:na]
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'createACar': Instantiation of supplied bean failed
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainFromSupplier(AbstractAutowireCapableBeanFactory.java:1223) ~[demo:6.1.5]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1161) ~[demo:6.1.5]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:562) ~[demo:6.1.5]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522) ~[demo:6.1.5]
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326) ~[demo:6.1.5]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[demo:6.1.5]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324) ~[demo:6.1.5]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[demo:6.1.5]
    at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) ~[demo:6.1.5]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1443) ~[demo:6.1.5]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1353) ~[demo:6.1.5]
    at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:904) ~[na:na]
    at org.springframework.beans.factory.support.RegisteredBean.resolveAutowiredArgument(RegisteredBean.java:229) ~[na:na]
    at org.springframework.beans.factory.aot.BeanInstanceSupplier.resolveAutowiredArgument(BeanInstanceSupplier.java:341) ~[na:na]
    ... 20 common frames omitted
Caused by: java.lang.UnsupportedOperationException: CGLIB runtime enhancement not supported on native image. Make sure to include a pre-generated class on the classpath instead: com.example.demo.config.BeanA$$SpringCGLIB$$FastClass$$0
    at org.springframework.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:363) ~[demo:6.1.5]
    at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData.lambda$new$1(AbstractClassGenerator.java:107) ~[na:na]
    at org.springframework.cglib.core.internal.LoadingCache.lambda$createEntry$1(LoadingCache.java:52) ~[na:na]
    at java.base@17.0.10/java.util.concurrent.FutureTask.run(FutureTask.java:264) ~[demo:na]
    at org.springframework.cglib.core.internal.LoadingCache.createEntry(LoadingCache.java:57) ~[na:na]
    at org.springframework.cglib.core.internal.LoadingCache.get(LoadingCache.java:34) ~[na:na]
    at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:130) ~[na:na]
    at org.springframework.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:317) ~[demo:6.1.5]
    at org.springframework.cglib.reflect.FastClass$Generator.create(FastClass.java:70) ~[na:na]
    at org.springframework.cglib.proxy.MethodProxy.helper(MethodProxy.java:148) ~[demo:6.1.5]
    at org.springframework.cglib.proxy.MethodProxy.init(MethodProxy.java:89) ~[demo:6.1.5]
    at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:256) ~[demo:6.1.5]
    at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331) ~[na:na]
    at com.example.demo.config.BeanConfig$$SpringCGLIB$$0.createACar(<generated>) ~[demo:na]
    at com.example.demo.config.BeanA__BeanDefinitions.lambda$getCreateACarInstanceSupplier$0(BeanA__BeanDefinitions.java:19) ~[na:na]
    at org.springframework.util.function.ThrowingFunction.apply(ThrowingFunction.java:63) ~[demo:6.1.5]
    at org.springframework.util.function.ThrowingFunction.apply(ThrowingFunction.java:51) ~[demo:6.1.5]
    at org.springframework.beans.factory.aot.BeanInstanceSupplier.lambda$withGenerator$0(BeanInstanceSupplier.java:171) ~[na:na]
    at org.springframework.util.function.ThrowingBiFunction.apply(ThrowingBiFunction.java:68) ~[demo:6.1.5]
    at org.springframework.util.function.ThrowingBiFunction.apply(ThrowingBiFunction.java:54) ~[demo:6.1.5]
    at org.springframework.beans.factory.aot.BeanInstanceSupplier.lambda$get$2(BeanInstanceSupplier.java:206) ~[na:na]
    at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:58) ~[demo:6.1.5]
    at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:46) ~[demo:6.1.5]
    at org.springframework.beans.factory.aot.BeanInstanceSupplier.invokeBeanSupplier(BeanInstanceSupplier.java:218) ~[na:na]
    at org.springframework.beans.factory.aot.BeanInstanceSupplier.get(BeanInstanceSupplier.java:206) ~[na:na]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.obtainInstanceFromSupplier(DefaultListableBeanFactory.java:949) ~[demo:6.1.5]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainFromSupplier(AbstractAutowireCapableBeanFactory.java:1217) ~[demo:6.1.5]
    ... 33 common frames omitted

Comment From: snicoll

I've made good progress on this one with AOT now recognizing the default method from the parent and generating the appropriate code for the target class, e.g.

/**
 * Bean definitions for {@link BeanConfig}.
 */
@Generated
public class BeanConfig__BeanDefinitions {
  /**
   * Get the bean definition for 'beanConfig'.
   */
  public static BeanDefinition getBeanConfigBeanDefinition() {
    RootBeanDefinition beanDefinition = new RootBeanDefinition(BeanConfig.class);
    beanDefinition.setTargetType(BeanConfig.class);
    ConfigurationClassUtils.initializeConfigurationClass(BeanConfig.class);
    beanDefinition.setInstanceSupplier(BeanConfig$$SpringCGLIB$$0::new);
    return beanDefinition;
  }

  /**
   * Get the bean instance supplier for 'createACar'.
   */
  private static BeanInstanceSupplier<Car> getCreateACarInstanceSupplier() {
    return BeanInstanceSupplier.<Car>forFactoryMethod(BeanConfig.class, "createACar")
            .withGenerator((registeredBean) -> registeredBean.getBeanFactory().getBean(BeanConfig.class).createACar());
  }

  /**
   * Get the bean definition for 'createACar'.
   */
  public static BeanDefinition getCreateACarBeanDefinition() {
    RootBeanDefinition beanDefinition = new RootBeanDefinition(Car.class);
    beanDefinition.setInstanceSupplier(getCreateACarInstanceSupplier());
    return beanDefinition;
  }
}

Unfortunately, it still does not work as the core container attempts to create a CGLIB proxy for the interface. It should not and that probably deserves its own issue.

Comment From: snicoll

@MartinLei that fixed the sample you've shared but please give that a try on your project when you get a chance.

Comment From: MartinLei

I checked it on the real project. It works like a charm.

Merci @snicoll

Comment From: jzhn

I know the root cause is probably different, just want to link the new issue https://github.com/spring-projects/spring-framework/issues/34329 here which shares similar symptom and workarounds.

Thanks for the well documented reproduciable projects as well as root cause analysis here which helped us to narrow down the issue.