We've been working on startup performance internally and noticed bean post-processors in particular caused us a significant amount of class loading making lazy initialization of beans less effective due to their use of reflection causing quite a bit of early classloading.
I wrote an internal extension to MetadataReader
that gave us parity with reflection for our internal processor implementations. I noticed org.springframework.context.annotation.ConfigurationClassParser
already uses a ResourceLoader
aware cached reader, so we're reading all of the bean classes once anyway, and could make our bean post-processors ResourceLoaderAware
and avoid repeated reading overhead.
Seemed to me this would benefit you upstream, for instance, uses of MethodIntrospector
like ScheduledBeanLazyInitializationExcludeFilter cause similar problems, loading hundreds of classes in the application I'm looking at:
Figured I'd run this by you upstream at this stage to get some feedback on what else would be required to get this landed so we're not maintaining a partial fork of this logic, and you can benefit from replacing reflection with class reading where it makes sense. I plan on taking tests I have here and roll them into the complete reflection vs classreading test suites you have, but was avoiding that work until I have some feedback that this is a direction you'd like to go in.
We've found using MetadataReader
with BeanDefinition
requires some boilerplate, that is less of a concern with reflection, so would be interested in if you think there should be something in BeanDefinition
to do at least the "real" class name resolution:
String beanClassName = beanDefinition.getBeanClassName();
if (beanDefinition instanceof AbstractBeanDefinition) {
// Deal with generated classes
AbstractBeanDefinition abstractBeanDefinition = (AbstractBeanDefinition) beanDefinition;
if (abstractBeanDefinition.hasBeanClass()) {
beanClassName = ClassUtils.getUserClass(abstractBeanDefinition.getBeanClass()).getName();
}
}
if (beanClassName == null && beanDefinition instanceof AnnotatedBeanDefinition) {
MethodMetadata factoryMethodMetadata = ((AnnotatedBeanDefinition) beanDefinition)
.getFactoryMethodMetadata();
Objects.requireNonNull(factoryMethodMetadata,
"No suitable factory method for beanDefinition for bean: " + beanName);
beanClassName = factoryMethodMetadata.getReturnTypeName();
}
if (beanClassName == null {
return null;
}
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(beanClassName);
return metadataReader.get().getAnnotationMetadata();
Comment From: pivotal-cla
@DanielThomas Please sign the Contributor License Agreement!
Click here to manually synchronize the status of this Pull Request.
See the FAQ for frequently asked questions.
Comment From: pivotal-cla
@DanielThomas Thank you for signing the Contributor License Agreement!
Comment From: DanielThomas
We'll address these cases with AOT.
Comment From: bclozel
For people wondering about the status of this issue.
While this PR yields nice startup improvements for reflection-heavy applications, this requires maintaining an entire ASM-based introspection implementation in Framework; we also think that the AOT feature can produce better results, as configuration parsing in general (i.e., not only reflection) is completely bypassed at runtime. We are actively working with Daniel and his team to investigate on the AOT approach and better unlock this for non-native applications.
See #30460, #30434 and #30410