spring-projects/spring-framework

Perform basic property determination without java.beans.Introspector

Closed this issue · 5 comments

Out of a discussion in #26884, we should replace our beans introspection with a basic property determination algorithm that discovers the common scenarios for Spring applications but nothing more: no indexed properties, no JDK-level customizers, etc. This significantly streamlines the discovery process on startup and allows for straightforward usage in a native image (not relying on Introspector substitutions).

The implementation that is about to be pushed for RC2 gets rid of all common java.beans.Introspector usage points in the codebase, replacing them with calls to PropertyDescriptorUtils.determineBasicProperties and StringUtils.uncapitalizeAsProperty. The entire core test suite passes with this.

Standard JavaBeans Introspector usage can be enforced through StandardBeanInfoFactory configuration in a META-INF/spring.factories file (a mechanism which existed for BeanInfo customizations before) with the following content:
org.springframework.beans.BeanInfoFactory=org.springframework.beans.StandardBeanInfoFactory
For Spring 5.3 compatible extended introspection including non-void setter methods:
org.springframework.beans.BeanInfoFactory=org.springframework.beans.ExtendedBeanInfoFactory
Neither of the two should be commonly necessary.

Our initial experience with this is pretty compelling, both for native images and for regular JVM deployments. In microbenchmarks, the new "basic" discovery is about 10 times faster than "standard" Introspector usage when spring.beaninfo.ignore=true (the optimization flag available before) has been set - against default Introspector settings, the new algorithm is even about 16 times faster.

It's worth noting that the algorithm can be customized in 5.3 as well, using the BeanInfoFactory mechanism. Plugging in a custom SimpleBeanInfoFactory with the equivalent of our "basic" algorithm should work for 5.3 as-is, ideally ordering it before ExtendedBeanInfoFactory (the 5.3 implementation that handles non-void setter methods) since it covers that part as well.

We could potentially backport such a SimpleBeanInfoFactory as an optional variant for configuration in a custom META-INF/spring.factories file, for performance-critical scenarios where the spring.beaninfo.ignore=true property does not make enough difference. If the wider integration tests against 6.0 do not reveal problems, we could even do so for 5.3.24 already.

Have you measured the memory impact, by any chance?
I wouldn't have anything against this to be backported - on the contrary. I wrote a little agent for testing reasons that instrumented/transformed Introspector.findCustomizerClass to simply return null and that showed ~5-20% (on average I'd say little less than 10%) improvement for the test suites I have at hand. For normal startups and deployments it's barely noticeable unfortunately. But it's great to hear that you see positive results.

We're substituting the same thing for native images: Introspector.findCustomizerClass to return null. We might keep doing this even with our new introspection algorithm in place, for third-party use of Introspector to be covered.

No memory numbers yet. The footprint is as minimal as possible now, so I would expect a measurable difference. Like with startup time, not sure whether it will really matter in regular application bootstrap, but it's the best we can do in terms of streamlining for the introspection functionality that 99.9% of Spring-based applications actually need.

For the time being, I've extracted a SimpleBeanInfoFactory as an internal delegate for SimpleBeanInfo adaptation in 6.0. This is also prepared for a straightforward backport to 5.3.x whenever we decide to proceed with it: picking a slightly modified version of SimpleBeanInfoFactory (turning public) along with a slightly modified version of PropertyDescriptorUtils.determineBasicProperties (using Introspector.decapitalize instead of StringUtils.uncapitalizeAsProperty).

For completeness reasons if someone is interested: https://github.com/dreis2211/remove-introspector-customizer-agent
I've put my little agent in a project for the interested bunch and poor souls that might not be able to try the goodness that @jhoeller is crafting 😉

The 5.3.x backport should be available in the current snapshot there now: #29330