Summary
A new change introduced in spring-context 6.2.0, which is shipped with springboot 3.4.0, creates StackOverflowError during spring-aot phase for java native applications, due to a newly added recursive processing behaviour.
Steps to reproduce
- checkout minimum reproduciable project, https://github.com/jzhn/spring-aot-stackoverflow/tree/main
- run
mvn compile spring-boot:process-aot
Expected behaviour
above command completes successfully, java native related files are generated in ./target/spring-aot
Actual behaviour
spring-aot fails with Stackoverflow, with truncated stacktrace as below.
[INFO] --- spring-boot:3.4.0:process-aot (default-cli) @ demo ---
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.4.0)
2024-12-05T15:54:51.999-08:00 INFO 74371 --- [demo] [ main] com.example.demo.DemoApplication : Starting DemoApplication using Java 21.0.5 with PID 74371 (/Users/jzhang/workhome/spring-aot-stackoverflow/target/classes started by jzhang in /Users/jzhang/workhome/spring-aot-stackoverflow)
2024-12-05T15:54:52.000-08:00 INFO 74371 --- [demo] [ main] com.example.demo.DemoApplication : No active profile set, falling back to 1 default profile: "default"
Exception in thread "main" java.lang.StackOverflowError
at java.base/java.lang.reflect.InvocationTargetException.<init>(InvocationTargetException.java:68)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:118)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at org.springframework.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:281)
at org.springframework.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:265)
at org.springframework.core.SerializableTypeWrapper$TypeProxyInvocationHandler.invoke(SerializableTypeWrapper.java:209)
at org.springframework.core.$Proxy6.getActualTypeArguments(Unknown Source)
at org.springframework.core.ResolvableType.getGenerics(ResolvableType.java:809)
at org.springframework.core.ResolvableType.getGeneric(ResolvableType.java:762)
at org.springframework.validation.beanvalidation.BeanValidationBeanRegistrationAotProcessor$BeanValidationDelegate.lambda$processAheadOfTime$0(BeanValidationBeanRegistrationAotProcessor.java:162)
at org.springframework.util.ReflectionUtils.doWithFields(ReflectionUtils.java:728)
at org.springframework.util.ReflectionUtils.doWithFields(ReflectionUtils.java:707)
at org.springframework.validation.beanvalidation.BeanValidationBeanRegistrationAotProcessor$BeanValidationDelegate.processAheadOfTime(BeanValidationBeanRegistrationAotProcessor.java:150)
at org.springframework.validation.beanvalidation.BeanValidationBeanRegistrationAotProcessor$BeanValidationDelegate.lambda$processAheadOfTime$0(BeanValidationBeanRegistrationAotProcessor.java:170)
at org.springframework.util.ReflectionUtils.doWithFields(ReflectionUtils.java:728)
at org.springframework.util.ReflectionUtils.doWithFields(ReflectionUtils.java:707)
at org.springframework.validation.beanvalidation.BeanValidationBeanRegistrationAotProcessor$BeanValidationDelegate.processAheadOfTime(BeanValidationBeanRegistrationAotProcessor.java:150)
at org.springframework.validation.beanvalidation.BeanValidationBeanRegistrationAotProcessor$BeanValidationDelegate.lambda$processAheadOfTime$0(BeanValidationBeanRegistrationAotProcessor.java:170)
Notes
- The version is reproduciable with Springboot 3.4.0, but it's okay with Springboot 3.3.6.
- Suspected commit: https://github.com/spring-projects/spring-framework/commit/357dbc035438e6ca718fd03f9facd6a64b2545c5, where the recursive processing logic is added.
Comment From: jzhn
- duplicate of #33936