24

In my project, I am trying to migrate all usages of

Foo foo = (Foo) beanFactory.getBean("name");

into

Foo foo = beanFactory.getBean(Foo.class);

The benefits are obvious: type safety, less convoluted code, less useless constants, etc. Typically such lines are located in static legacy contexts where such a wiring is the only option.

This was all fine until one day users started to complain about the slowness which turned out to come from Spring internals. So I fired up a profiler to find a hotspot in

org.springframework.beans.factory.support.AbstractBeanFactory::doGetBean(String, Class<T>, Object[], boolean)

which has an expensive call to

Class.isAssignableFrom(anotherClass).

I have quickly created a small performance test to find out the speed difference between string name and type lookups is a whooping 350 times (I'm using StaticApplicationContext for this test FAIW)!

While investigating this, I found SPR-6870 which has high number of votes but for some reason isn't addressed. This led me to an attempt to solve this problem which does significantly improve the situation but is still slower ~25 times than lookup by String! It turns out this attempt only solves half of the problem: it caches the name of the bean to save on O(n) iteration but still has to do call isAssignableFrom to validate the type.

Described problem is not only related to my scenario but is also for beans which use @Autowired and can be felt hard in cases where beans are created inside a loop.

One of solutions would be to override one of the bean factory methods and cache the is-this-bean-of-the-same-type check results but clearly this should be done in Spring and not in my own code.

Is anyone else suffering from a similar problem and found a solution to it?

mindas
  • 26,463
  • 15
  • 97
  • 154
  • So, you want to autowire by type, but without doing any type checking? – GreyBeardedGeek Feb 24 '12 at 14:59
  • I want the second call against the same type to avoid the expensive type checking. Or at least I want an ability to specify whether this is enabled or disabled. Object creation is such a basic thing that small optimizations like this can make a big difference. – mindas Feb 24 '12 at 15:02

2 Answers2

5

This problem is now solved in Spring with the resolution of SPR-6870. See the resolution comments there for details. The fix is available as of versions 3.2.0.RELEASE and 3.1.2.

Chris Beams
  • 1,473
  • 10
  • 13
  • 1
    We are using spring 3.2.1 but still most of the time spent by the application during start up is autowiring by type. Any suggestions? – Andy Dufresne May 31 '13 at 09:39
2

Most Spring apps wire things together on startup, rather than grabbing beans from the context at runtime. Even still, unless you are changing your application context a lot during the regular running of your app, you should not ever fetch a bean more than once.

Given that, if your users are complaining of slowness, it seems your real problem is too many bean lookups; your use of a slower means of doing so has just surfaced the real problem.

I would try moving to Java Config (configure your dependencies in Java, supported in Spring 3.0 I believe) and configuring your app to wire all beans at startup. This also has the advantage that your app just won't start if dependencies can't be met.

davetron5000
  • 24,123
  • 11
  • 70
  • 98
  • I think what you wanted to really say is "my problem is that I am using Spring for this" :) Regarding Java Config - yes, this is an escape option, but I still think the original problem should be addressed as it seem to affect (judging from the upvote count) lots of other Spring users. – mindas Feb 24 '12 at 22:08
  • I never had this problem with Spring and used it on some pretty large projects. The app startup time was quite slow, but once things were wired up, it worked fine. We autowired by name typically, but had some XML configuration as well. We never called getBean directly. – davetron5000 Feb 25 '12 at 01:50
  • "Large projects" can have different scenarios. Your argument that you never had this problem is moot. Moreover, I doubt the suggestion that using Java Config (instead of XML, for example) will help in any way - if the same type of BeanFactory is used, it will suffer from the same problem. It's the factory bean instantiation implementation that is broken, and not the assembly. – mindas Feb 25 '12 at 11:13
  • 1
    What I'm saying is that you pay the price of instantiation up front on startup, not every time the user uses your app. This may be a fundamental design change in your application, but it is not idiomatic to query the context for beans during the normal runtime operations of your app. – davetron5000 Feb 25 '12 at 16:30