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.