Nicolas Frankel is an IT consultant with 10 years experience in Java / JEE environments. He likes his job so much he writes technical articles on his blog and reviews technical books in his spare time. He also tries to find other geeks like him in universities, as a part-time lecturer. Nicolas is a DZone MVB and is not an employee of DZone and has posted 228 posts at DZone. You can read more from them at their website. View Full User Profile

Method injection with Spring

07.30.2012
| 28540 views |
  • submit to reddit

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:

public class Singleton {

    private Prototype prototype;

    public Singleton(Prototype prototype) {

        this.prototype = prototype;
    }

    public void doSomething() {

        prototype.foo();
    }

    public void doSomethingElse() {

        prototype.bar();
    }
}
The next snippet displays the correct code:
public abstract class Singleton {

    protected abstract Prototype createPrototype();

    public void doSomething() {

        createPrototype().foo();
    }

    public void doSomethingElse() {

        createPrototype().bar();
    }
}
As you noticed, code doesn't specify the createPrototype() implementation. This responsibility is delegated to Spring, hence the following needed configuration:
<bean id="prototype" class="ch.frankel.blog.Prototype" scope="prototype" />

<bean id="singleton" class="sample.MySingleton">
	<lookup-method name="createPrototype" bean="prototype" />
</bean>
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:
  • 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)
To go further:


Published at DZone with permission of Nicolas Frankel, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Harald Wellmann replied on Mon, 2012/07/30 - 5:17pm

This is actually one of many reasons to turn from Spring to CDI, where injection of mixed scopes works out of the box.

Henk De Boer replied on Tue, 2012/07/31 - 4:35am in response to: Harald Wellmann

I second that, CDI works much better overall and especially here because of its proxy based contextual injection (the C that CDI added to plain DI).

joshua long replied on Tue, 2012/07/31 - 9:19am

Spring also provides support for proxy based beans, and has since Spring 2.0 which is many (5+) years old. 

 If you're using the XML notation, then it looks like this:

 

<bean class = "A" id ="a"  scope = "session">

    <aop:scoped-proxy/>

</bean> 

 

<bean class = "B" id ="b" >

   <property name = "a" ref = "a"/> 

</bean> 

 

If you're using the Java configuration:

 

@Bean

@Scope(value= "session", proxyMode = ScopedProxyMode.INTERFACES )

public A a(){

  return new A(); 

 } 

 

@Bean

public B b (){

   B b = new B();

   b.setA( a() );

   return b;  

 } 

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.