Affects: 5.2+
My organization has multiple high-traffic, customer-facing applications that create many beans per request. The individual creations are fast enough but in aggregate these beans contribute noticeably to the request's critical path latency, as well as the per-request cpu. The key portion of the stack trace is:
org.springframework.beans.factory.support.ConstructorResolver.resolvePreparedArguments
org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod
org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance
Eventual leaf nodes of the call graph are:
java.lang.Class.forName0
java.lang.reflect.Constructor.getParameterAnnotations
java.lang.reflect.Method.getParameterAnnotations
org.springframework.core.annotation.AnnotationUtils.getValue
Annotations and classes don't change during runtime, yet they are repeatedly processed.
Solution
Some level of caching is appropriate, either multiple caches (one per leaf) or a single larger one around ConstructorResolver.resolvePreparedArguments. Multiple specific caches seems preferable because of lower contention and simpler keys.
Supporting Data
Below are further, detailed stack traces that illustrate the path from ConstructorResolver.resolvePreparedArguments to the leaf nodes.
org.springframework.core.annotation.AnnotationUtils.getValue
org.springframework.core.annotation.AnnotationUtils.getValue
org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver.checkQualifiers
org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver.isAutowireCandidate
org.springframework.beans.factory.support.DefaultListableBeanFactory.isAutowireCandidate
org.springframework.beans.factory.support.DefaultListableBeanFactory.isAutowireCandidate
org.springframework.beans.factory.support.DefaultListableBeanFactory.isAutowireCandidate
org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates
org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency
org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency
org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument
org.springframework.beans.factory.support.ConstructorResolver.resolvePreparedArguments
java.lang.reflect.Constructor.getParameterAnnotations
org.springframework.core.MethodParameter.getParameterAnnotations
org.springframework.beans.factory.InjectionPoint.getAnnotations
org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver.isLazy
org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver.getLazyResolutionProxyIfNecessary
org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency
org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument
org.springframework.beans.factory.support.ConstructorResolver.resolvePreparedArguments
java.lang.reflect.Method.getParameterAnnotations
org.springframework.core.MethodParameter.getParameterAnnotations
org.springframework.beans.factory.InjectionPoint.getAnnotations
org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver.isLazy
org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver.getLazyResolutionProxyIfNecessary
org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency
org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument
org.springframework.beans.factory.support.ConstructorResolver.resolvePreparedArguments
java.lang.Class.forName0
java.lang.Class.forName
org.springframework.util.ClassUtils.forName
org.springframework.beans.factory.config.MethodInvokingBean.resolveClassName
org.springframework.util.MethodInvoker.prepare
org.springframework.beans.factory.config.MethodInvokingFactoryBean.afterPropertiesSet
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.lambda$invokeInitMethods$5
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory$Lambda.run
java.security.AccessController.doPrivileged
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean
<proprietary BeanFactory subclass>.createBean
org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$1
org.springframework.beans.factory.support.AbstractBeanFactory$Lambda.getObject
<proprietary request scope subclass>.createBean
<proprietary request scope subclass>.get
org.springframework.beans.factory.support.AbstractBeanFactory.getBean
org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference
org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary
org.springframework.beans.factory.support.ConstructorResolver.resolvePreparedArguments
Affects: 5.2+
My organization has multiple high-traffic, customer-facing applications that create many beans per request. The individual creations are fast enough but in aggregate these beans contribute noticeably to the request's critical path latency, as well as the per-request cpu. The key portion of the stack trace is:
Eventual leaf nodes of the call graph are:
java.lang.Class.forName0java.lang.reflect.Constructor.getParameterAnnotationsjava.lang.reflect.Method.getParameterAnnotationsorg.springframework.core.annotation.AnnotationUtils.getValueAnnotations and classes don't change during runtime, yet they are repeatedly processed.
Solution
Some level of caching is appropriate, either multiple caches (one per leaf) or a single larger one around
ConstructorResolver.resolvePreparedArguments. Multiple specific caches seems preferable because of lower contention and simpler keys.Supporting Data
Below are further, detailed stack traces that illustrate the path from ConstructorResolver.resolvePreparedArguments to the leaf nodes.