Description
Mattias Nissler opened SPR-5658 and commented
At work, we've hit a bug in an application that:
a) I have only seen once, so cannot reproduce
b) I'm quite sure is caused by an @Autowired
setter called with a null argument (which should be impossible)
When analyzing the problem, we found that three instances of the bean being autowired were instantiated in rapid succession (i.e. concurrently). This made us think of checking whether there maybe concurrency issues within Springs bean initialization code. Ultimately I looked at the code in AutowiredAnnotationBeanPostProcessor.AutowiredMethodElement.inject() and I think I came up with a race condition scenario that does explain the injected null reference:
Consider AutowiredAnnotationBeanPostProcessor.AutowiredMethodElement.inject():
Let's say there are 3 Threads: t1, t2, t3 and the AutowiredMethodElement is in the state cached == false, cachedMethodArguments == null. This means that there probably hasn't been any call to inject() before and checking our log files, we indeed found that we didn't instantiate a bean of this type before. Now what I guess could have happened is:
- t1 enters, finds cached == false, steps into else block and is interrupted
- t2 enters, finds cached == false, steps into else block and is interrupted
- t1 continues, fills in cachedMethodArguments, sets cached = true
- t3 enters, finds cached == true, steps into if block and is interrupted
- t2 continues, executes cachedMethodArguments = new Object[...] and is interrupted (chachedMethodArguments contains null references now!)
- t3 continues, copies cachedMethodArguments[i] to arguments[i] (all null)
- t3 invokes
@Autowired
annotated method, thereby injecting the null value
Have I missed any synchronization mechanism that prevents this? If not, it's probably a bug that needs fixing ;-)
Affects: 2.5.6
Referenced from: commits a2fdb68