BeansBinding Performance (Issue 37)
Since the release of NetBeans 6.0, the BeansBinding (JSR 295) framework has gained popularity. Personally, I've adopted it in all my projects due to its ease of use and productivity benefits. However, it's not without issues. The project has been stagnant for over a year, and recent news about Swing's future (here's an example) has only added to the uncertainty.
Despite this, the inclusion of BeansBinding in NetBeans and its foundational role in JavaFx suggests it isn't going away anytime soon. Yet, one issue with BeansBinding can be particularly frustrating: performance bottlenecks.
The Problem: Slow Form Loading
Getting into the subject, BeansBinding has not been updated for a while now and has some bugs that are really annoying.
When binding a form with 20+ fields using BeansBinding (via NetBeans + Matisse), you might notice sluggish load times.
Profiling reveals that the culprit lies in the bindingGroup.bind()
call during initComponents()
.
A deeper dive into the BeanProperty
and ElProperty
classes uncovers the root cause: the getBeanInfo(Object object)
method, which invokes Introspector.getBeanInfo(object.getClass(), Introspector.IGNORE_ALL_BEANINFO)
, a time-consuming operation.
Investigating the Issue
A brief search led me to a mail thread discussing this very issue.
The thread references Issue 37 and suggests using Introspector.getBeanInfo(Class class)
directly, which leverages cached metadata and significantly boosts performance.
The Solution: Runtime Bytecode Injection with Javassist
Your initial approach could be to get the source code and replace the code fragment you need and then recompile the library. There is a great tutorial (Hacking Java Libraries) that teaches you how to do this if you don't know how to do it by yourself. The article is an excerpt of "Covert Java" by Alex Kalinovsky (Sams Publishing, May 2004) (recommended reading).
Rather than modifying and recompiling the library, an approach that's cumbersome and hard to maintain, I opted for runtime bytecode manipulation using the Javassist library. Javassist provides a lightweight and flexible way to modify classes on the fly.
Here’s the code to patch the getBeanInfo
method in ElProperty
and BeanProperty
:
try {
ClassPool cp = ClassPool.getDefault();
CtClass cc = cp.get("org.jdesktop.beansbinding.ELProperty");
CtMethod m = cc.getDeclaredMethod("getBeanInfo");
m.setBody("{" +
//"assert $1 != null;"
"try {" +
"return java.beans.Introspector.getBeanInfo($1.getClass());" +
"} catch (java.beans.IntrospectionException ie) {" +
"throw new org.jdesktop.beansbinding.PropertyResolutionException(\\"Exception while introspecting \\" + $1.getClass().getName(), ie);" +
"} }");
Class c = cc.toClass();
cc = cp.get("org.jdesktop.beansbinding.BeanProperty");
m = cc.getDeclaredMethod("getBeanInfo");
m.setBody("{" +
//"assert $1 != null;" +
"try {" +
"return java.beans.Introspector.getBeanInfo($1.getClass());" +
"} catch (java.beans.IntrospectionException ie) {" +
"throw new org.jdesktop.beansbinding.PropertyResolutionException(\\"Exception while introspecting \\" + $1.getClass().getName(), ie);" +
"} }");
c = cc.toClass();
} catch (NotFoundException ex) {
// log exception
} catch (CannotCompileException ex) {
// log exception
}
This replacement modifies the getBeanInfo
method to use the cached Introspector.getBeanInfo(Class class)
by default,
significantly improving performance.
Important Notes
- Ensure this code runs before any BeansBinding classes are loaded, or it won't have any effect. If needed, refer to the Javassist tutorial for tips on building a class loader.
- For advanced scenarios, HotSwapper can be used to swap classes dynamically.
Closing Thoughts
It's unclear why Shannon Hickey chose Introspector.IGNORE_ALL_BEANINFO initially
.
However, for most use cases, using cached metadata is both safe and efficient.
I hope this solution helps you enhance your BeansBinding applications. Feel free to share comments, suggestions, or improvements!
Comments in "BeansBinding Performance (Issue 37)"
Thanks for the note. I hadn't even noticed,the issue appears as unfixed. I used to check the subversion, but since there was no point (everything remains the same) I don't check in a regular basis.
As you say, the issue was fixed 4 weeks ago ELPropery
In either case, this is a good example showing how to use javassist.
Thank you for your patch! I've got dialog with nearly 70 items and it took more than 3 seconds to show it, but after I've applied your patch the time has been reduced to just around a second.
But today after finding and implementing your hack , everything works much much much faster and smoother.
Thank you very much for sharing this with all of us.
I really really owe you a lot. A beer??? No. The whole supermarket of beers!!!!!!!!!!!
Cheers!!!!!!!!