What
Spring boot uses AOT processing to generate required reflect/proxy/resource configs as well as CGLIB proxy classes in compile time. For beans defined with interface types, CGLIB proxy is usually unnecessary so process-aot job does not generate CGLIB proxy.
When org.springframework.data:spring-data-mongodb
is imported, java native projects with repository beans defined with interface types will encounter exception in runtime about unexpected CGLIB usage for the repository class.
Update: upon further investigation, just having org.springframework:spring-tx
is enough to trigger the issue. It's not directly caused by spring-data-mongodb
.
Minimum reproduciable project
https://github.com/jzhn/springNativeBugSpringDataMongoDB. Please refer to its README for steps to reproduce, expected behaviour and actual behaviour.
Versions
- JDK:
21.0.2-graalce
- Spring-Boot:
3.4.2
(latest to date) - MacOS
15.2
on ARM64 mac
Comment From: snicoll
@jzhn thanks for the report and the reproducer. Here is the stacktrace:
2025-01-28T10:19:50.461+01:00 WARN 27662 --- [demo] [ main] o.s.c.support.GenericApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'demoApplication': Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'myDao': Unexpected AOP exception
Application run failed
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'demoApplication': Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'myDao': Unexpected AOP exception
at org.springframework.beans.factory.aot.BeanInstanceSupplier.resolveAutowiredArgument(BeanInstanceSupplier.java:369)
at org.springframework.beans.factory.aot.BeanInstanceSupplier.resolveArguments(BeanInstanceSupplier.java:289)
at org.springframework.beans.factory.aot.BeanInstanceSupplier.get(BeanInstanceSupplier.java:223)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.obtainInstanceFromSupplier(DefaultListableBeanFactory.java:979)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainFromSupplier(AbstractAutowireCapableBeanFactory.java:1243)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1186)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:336)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:307)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:334)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.instantiateSingleton(DefaultListableBeanFactory.java:1122)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingleton(DefaultListableBeanFactory.java:1093)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:1030)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:987)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:627)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:318)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1361)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1350)
at com.example.demo.DemoApplication.main(DemoApplication.java:18)
at java.base@21.0.5/java.lang.invoke.LambdaForm$DMH/sa346b79c.invokeStaticInit(LambdaForm$DMH)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'myDao': Unexpected AOP exception
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:608)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:336)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:307)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:334)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1631)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1519)
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:913)
at org.springframework.beans.factory.support.RegisteredBean.resolveAutowiredArgument(RegisteredBean.java:253)
at org.springframework.beans.factory.aot.BeanInstanceSupplier.resolveAutowiredArgument(BeanInstanceSupplier.java:366)
... 23 more
Caused by: org.springframework.aop.framework.AopConfigException: Unexpected AOP exception
at org.springframework.aop.framework.CglibAopProxy.buildProxy(CglibAopProxy.java:243)
at org.springframework.aop.framework.CglibAopProxy.getProxy(CglibAopProxy.java:167)
at org.springframework.aop.framework.ProxyFactory.getProxy(ProxyFactory.java:110)
at org.springframework.aop.framework.AbstractAdvisingBeanPostProcessor.postProcessAfterInitialization(AbstractAdvisingBeanPostProcessor.java:127)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(AbstractAutowireCapableBeanFactory.java:439)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1815)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601)
... 34 more
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.MongoDao$$SpringCGLIB$$0
at org.springframework.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:363)
at org.springframework.cglib.proxy.Enhancer.generate(Enhancer.java:575)
at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData.lambda$new$1(AbstractClassGenerator.java:107)
at org.springframework.cglib.core.internal.LoadingCache.lambda$createEntry$1(LoadingCache.java:52)
at java.base@21.0.5/java.util.concurrent.FutureTask.run(FutureTask.java:317)
at org.springframework.cglib.core.internal.LoadingCache.createEntry(LoadingCache.java:57)
at org.springframework.cglib.core.internal.LoadingCache.get(LoadingCache.java:34)
at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:130)
at org.springframework.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:317)
at org.springframework.cglib.proxy.Enhancer.createHelper(Enhancer.java:562)
at org.springframework.cglib.proxy.Enhancer.createClass(Enhancer.java:407)
at org.springframework.aop.framework.ObjenesisCglibAopProxy.createProxyClassAndInstance(ObjenesisCglibAopProxy.java:62)
at org.springframework.aop.framework.CglibAopProxy.buildProxy(CglibAopProxy.java:228)
... 40 more
FTR, I don't believe that #32609 is linked to this.
Comment From: snicoll
I didn't look carefully enough at the sample and missed the comment you added in the configuration file:
// replace above with below to workaround the issue
// since defining bean with concreate type would
// make process-aot generate the required cglib proxy
// @Bean
// public MongoDao myDao() {
// return new MongoDao();
// }
This is what you should be doing. If you expose a type that does not provide the capabilities that are needed at runtime, Spring AOT will not get a chance to prepare the object to honor them.
Comment From: jzhn
@snicoll thanks for the quick triage and apologies for missing the doc about returning precise bean types.
For my learning - is it possible to implement some sort of generic logic to generate and register the CGLIB bean types manually? The reason I'm asking that is my org is doing a massive conversion to java native + springboot. So if there's a chance that such logic can be handled centrally, it could save teams some efforts of updating all of their configurations. For example, if teams know a certain package, like com.foo.repository
, will contain Repository beans that would be exposed via interface typed beans in configurations, then we can do a class path scan and create CGLIB proxies for all classes during spring AOT.