Spring core comes out-of-the-box with two scopes: singletons and
prototypes. Singletons implement the Singleton pattern, meaning there's
only a single instance at runtime (in a JVM). Spring instantiates them
during context creation, caches them in the context, and serves them
from the cache when needed (or something like that). Prototypes are
instantiated each time you access the context to get the bean.
Problems arise when you need to inject a prototype-scoped bean in a singleton-scoped bean. Since singletons are created (and then injected) during context creation: it's the only time the Spring context is accessed and thus prototype-scoped beans are injected only once, thus defeating their purpose. In order to inejct prototypes into singletons, and side-by-syde with setter and constructor injection, Spring proposes another way for injection, called method injection. It works in the following way: since singletons are instantiated at context creation, it changes the way prototype-scoped are handled, from injection to created by an abstract method. The following snippet show the unsuccessful way to achieve injection:
The next snippet displays the correct code:
As
you noticed, code doesn't specify the createPrototype() implementation.
This responsibility is delegated to Spring, hence the following needed
configuration:
Note
that an alternative to method injection would be to explicitly access
the Spring context to get the bean yourself. It's a bad thing to do
since it completely defeats the whole Inversion of Control pattern, but
it works (and is essentially the only option when a nasty bug happens on
the server - see below).
However, using method injection has several main limitations:
Problems arise when you need to inject a prototype-scoped bean in a singleton-scoped bean. Since singletons are created (and then injected) during context creation: it's the only time the Spring context is accessed and thus prototype-scoped beans are injected only once, thus defeating their purpose. In order to inejct prototypes into singletons, and side-by-syde with setter and constructor injection, Spring proposes another way for injection, called method injection. It works in the following way: since singletons are instantiated at context creation, it changes the way prototype-scoped are handled, from injection to created by an abstract method. The following snippet show the unsuccessful way to achieve injection:
01.public class Singleton {02. 03.private Prototype prototype;04. 05.public Singleton(Prototype prototype) {06. 07.this.prototype = prototype;08.}09. 10.public void doSomething() {11. 12.prototype.foo();13.}14. 15.public void doSomethingElse() {16. 17.prototype.bar();18.}19.}01.public abstract class Singleton {02. 03.protected abstract Prototype createPrototype();04. 05.public void doSomething() {06. 07.createPrototype().foo();08.}09. 10.public void doSomethingElse() {11. 12.createPrototype().bar();13.}14.}1.<bean id="prototype" class="ch.frankel.blog.Prototype" scope="prototype" />2. 3.<bean id="singleton" class="sample.MySingleton">4.<lookup-method name="createPrototype" bean="prototype" />5.</bean>- Spring achieves this black magic by changing bytecode. Thus, you'll need to have the CGLIB libraryon the classpath.
- The feature is only available by XML configuration, no annotations (see this JIRAfor more information)
- Finally, some application servers have bugs related to CGLIB (such as this one)
- Spring's documentation on method injection
