Description
A considerable startup regression over 30+ seconds was found in the Spring refresh phase after the migration to Spring 6.1.16
from Spring 5.3.x with no other changes.
For every Configuration/Proxy bean discovered by Spring 6.x, three classpath lookups are triggered. Since these classes are not on the classpath, it causes a full classpath scan, ultimately resulting in a lookup failure (causing the delay).
One such example for the lookup for an application bean:
com.app.FooConfigBean$$SpringCGLIB$$0
com.app.FooConfigBean$$SpringCGLIB$$FastClass$$0
com.app.FooConfigBean$$SpringCGLIB$$FastClass$$1
Stack Trace for com.app.FooConfigBean$$SpringCGLIB$$0
lookup:
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526)
at java.base/java.lang.Class.forName0(Native Method)
at java.base/java.lang.Class.forName(Class.java:534)
at java.base/java.lang.Class.forName(Class.java:513)
at org.springframework.cglib.core.ReflectUtils.loadClass(ReflectUtils.java:569)
at org.springframework.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:351)
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/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.context.annotation.ConfigurationClassEnhancer.createClass(ConfigurationClassEnhancer.java:156)
at org.springframework.context.annotation.ConfigurationClassEnhancer.enhance(ConfigurationClassEnhancer.java:113)
at org.springframework.context.annotation.ConfigurationClassPostProcessor.enhanceConfigurationClasses(ConfigurationClassPostProcessor.java:534)
at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanFactory(ConfigurationClassPostProcessor.java:311)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:363)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:153)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:789)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:607)
Stack Trace for com.app.FooConfigBean$$SpringCGLIB$$FastClass$$0
lookup
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526)
at java.base/java.lang.Class.forName0(Native Method)
at java.base/java.lang.Class.forName(Class.java:534)
at java.base/java.lang.Class.forName(Class.java:513)
at org.springframework.cglib.core.ReflectUtils.loadClass(ReflectUtils.java:569)
at org.springframework.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:351)
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/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.reflect.FastClass$Generator.create(FastClass.java:70)
at org.springframework.cglib.proxy.MethodProxy.helper(MethodProxy.java:148)
at org.springframework.cglib.proxy.MethodProxy.init(MethodProxy.java:89)
at org.springframework.cglib.proxy.MethodProxy.create(MethodProxy.java:63)
at com.app.FooConfigBean$$SpringCGLIB$$0.CGLIB$STATICHOOK1(<generated>)
at com.app.FooConfigBean$$SpringCGLIB$$0.<clinit>(<generated>)
Given this behavior has changed with Spring 6.x, is there a way to bypass these class lookups that will never be found on the current classpath?
This situation is reminiscent of the BeanInfo/Customizer lookups, which also caused a similar regression that could be disabled.