Overview
When running various bean override tests in AOT mode (via AotIntegrationTests
), AOT processing fails with a stack trace similar to the following.
org.springframework.test.context.aot.TestContextAotException: Failed to generate AOT artifacts for test classes [org.springframework.test.context.bean.override.convention.TestBeanByTypeIntegrationTests]
at org.springframework.test.context.aot.TestContextAotGenerator.lambda$10(TestContextAotGenerator.java:281)
at java.base/java.util.LinkedHashMap.forEach(LinkedHashMap.java:721)
at org.springframework.util.MultiValueMapAdapter.forEach(MultiValueMapAdapter.java:179)
at org.springframework.test.context.aot.TestContextAotGenerator.processAheadOfTime(TestContextAotGenerator.java:244)
at org.springframework.test.context.aot.TestContextAotGenerator.processAheadOfTime(TestContextAotGenerator.java:205)
at org.springframework.test.context.aot.AotIntegrationTests.runEndToEndTests(AotIntegrationTests.java:166)
at org.springframework.test.context.aot.AotIntegrationTests.endToEndTestsForSelectedTestClasses(AotIntegrationTests.java:159)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.springframework.core.test.tools.CompileWithForkedClassLoaderExtension.intercept(CompileWithForkedClassLoaderExtension.java:97)
at org.springframework.core.test.tools.CompileWithForkedClassLoaderExtension.interceptTestMethod(CompileWithForkedClassLoaderExtension.java:83)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
Caused by: org.springframework.test.context.aot.TestContextAotException: Failed to process test class [org.springframework.test.context.bean.override.convention.TestBeanByTypeIntegrationTests] for AOT
at org.springframework.test.context.aot.TestContextAotGenerator.processAheadOfTime(TestContextAotGenerator.java:317)
at org.springframework.test.context.aot.TestContextAotGenerator.lambda$10(TestContextAotGenerator.java:273)
... 11 more
Caused by: java.lang.IllegalStateException: No constructor or factory method candidate found for Root bean: class [null]; scope=singleton; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodNames=null; destroyMethodNames=null and argument types []
at org.springframework.beans.factory.support.ConstructorResolver.resolveConstructorOrFactoryMethod(ConstructorResolver.java:996)
at org.springframework.beans.factory.support.RegisteredBean.resolveConstructorOrFactoryMethod(RegisteredBean.java:218)
at org.springframework.beans.factory.support.RegisteredBean.resolveInstantiationDescriptor(RegisteredBean.java:228)
at org.springframework.util.function.SingletonSupplier.get(SingletonSupplier.java:106)
at org.springframework.beans.factory.aot.DefaultBeanRegistrationCodeFragments.getTarget(DefaultBeanRegistrationCodeFragments.java:86)
at org.springframework.beans.factory.aot.BeanDefinitionMethodGenerator.generateBeanDefinitionMethod(BeanDefinitionMethodGenerator.java:85)
at org.springframework.beans.factory.aot.BeanRegistrationsAotContribution.lambda$3(BeanRegistrationsAotContribution.java:89)
at java.base/java.util.LinkedHashMap.forEach(LinkedHashMap.java:721)
at org.springframework.beans.factory.aot.BeanRegistrationsAotContribution.generateRegisterBeanDefinitionsMethod(BeanRegistrationsAotContribution.java:87)
at org.springframework.beans.factory.aot.BeanRegistrationsAotContribution.lambda$1(BeanRegistrationsAotContribution.java:72)
at org.springframework.aot.generate.GeneratedMethod.<init>(GeneratedMethod.java:54)
at org.springframework.aot.generate.GeneratedMethods.add(GeneratedMethods.java:112)
at org.springframework.aot.generate.GeneratedMethods.add(GeneratedMethods.java:89)
at org.springframework.beans.factory.aot.BeanRegistrationsAotContribution.applyTo(BeanRegistrationsAotContribution.java:71)
at org.springframework.context.aot.BeanFactoryInitializationAotContributions.applyTo(BeanFactoryInitializationAotContributions.java:78)
at org.springframework.context.aot.ApplicationContextAotGenerator.lambda$0(ApplicationContextAotGenerator.java:58)
at org.springframework.context.aot.ApplicationContextAotGenerator.withCglibClassHandler(ApplicationContextAotGenerator.java:67)
at org.springframework.context.aot.ApplicationContextAotGenerator.processAheadOfTime(ApplicationContextAotGenerator.java:53)
at org.springframework.test.context.aot.TestContextAotGenerator.processAheadOfTime(TestContextAotGenerator.java:314)
... 12 more
The key part is this:
No constructor or factory method candidate found for Root bean: class [null]; scope=singleton; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodNames=null; destroyMethodNames=null and argument types []
... which seems like a relatively empty BeanDefinition
.
Failing Test Classes
TestBeanByTypeIntegrationTests
TestBeanInheritanceIntegrationTests$ConcreteTestBeanIntegrationTests
MockitoBeanByTypeIntegrationTests
MockitoBeanIntegrationTests
Related Issues
- https://github.com/spring-projects/spring-boot/issues/32195
-
29122
-
32932
Comment From: snicoll
Quoting #32933:
Although @MockitoBean and @MockitoSpyBean will not work out-of-the-box within a GraalVM native image due to their dependency on Mockito, the general Bean Override support for tests should ideally work within a native image.
That is partially accurate. The mockito support in Spring Boot never worked in AOT mode on the JVM. It current fails as follows:
Caused by: org.springframework.aot.generate.UnsupportedTypeValueCodeGenerationException: Code generation does not support org.springframework.boot.test.mock.mockito.MockDefinition
at org.springframework.aot.generate.ValueCodeGenerator.generateCode(ValueCodeGenerator.java:113)
... 42 more
That is because the TCF requires the key for a context to be computed at a certain time and makes it very AOT unfriendly as a result. I think this issue needs to be requalified as we need to fix that.
Next up, the support works by registering a singleton with a "dummy" bean definition and that's why this is failing.
Comment From: snicoll
This issue is no longer relevant as it's obvious that bean overriding does not work with AOT. And we already have an issue to investigate how to fix that, see https://github.com/spring-projects/spring-framework/issues/32933