23
votes

I have a singleton bean which needs for each call of a function to return a reference to a different (new) prototype bean. The only way that I can think of doing this is to programmatically retrieve a new prototype bean instance from the BeanFactory/ApplicatioContext by invoking its getBean() method. Code sample will follow...

Is there a better way to do this? Only via configuration, hopefully? (Personally, I doubt there is...)

<bean id="protoBean" scope="prototype"
        class="com.blahblah.ProtoBean" />

<bean id="singletonBean"
        class="com.blahblah.SingletonBean" />

public class ProtoBean {

    ....
}

public class SingletonBean {

    private BeanFactory factory;

    public ProtoBean dispense() {
        return (ProtoBean) factory.getBean("protoBean");
    }

    ....
}
3

3 Answers

14
votes
10
votes

From Spring 3.0, we can use <aop:scoped-proxy> for dependency injection of the proper scope. Behind the scene, Spring injects proxied objects and is responsible for finding the right scope context, may it be prototype, session or request etc. See the official documentations here.

And to make life easier, Spring has also introduced proxyMode attribute for @Scope, so we are not limited to XML declarations only. For example:

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

Make sure to document clearly the injected bean is a proxy to warn others that getClass() and casting may not yield the expected result. Also, make sure equals() and hashCode() in the proxied class use access methods rather than directly accessing class variables.

4
votes

Using method injection makes the singleton-bean class difficult to unit-test (you need to create a subclass to implement the method which gives out the dependency). Plus it's less reusable because you can't directly instantiate it, so if you're not using Spring and want to use this class, you'll need to subclass and provide the bean-returning method.

A better approach IMHO is to use a proxy, a prototype target source and a prototype target bean, as follows. Such a singleton-bean class is easily unit-testable and better reusable.

<bean id="targetPooledObject" class="pool.PooledObject" scope="prototype">
    <constructor-arg value="42" />
</bean>

<bean id="prototypeTargetSource" class="org.springframework.aop.target.PrototypeTargetSource">
    <property name="targetBeanName" value="targetPooledObject" />
</bean>

<bean id="pooledObject" class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="targetSource" ref="prototypeTargetSource" />           
</bean>

<bean id="poolConsumer" class="pool.PoolConsumer">
    <property name="pooledObject" ref="pooledObject" />
</bean>

Now we can inject pooledObject into a singleton bean (poolConsumer as shown above), and for every method call that we make on that singleton bean, (e.g. every time we call poolConsumer.callPooledObjectMethod() which in turn calls pooledObject.foo()) we get a new PooledObject bean.

Following is the corresponding code:

public class PooledObject 
{
    private int x;

    public PooledObject(int x)
    {
        this.x = x;
    }

    public void foo()
    {
        System.out.println("foo called");
    }
}

public class PoolConsumer
{
    private PooledObject pooledObject;

    public PooledObject getPooledObject() 
    {
        return pooledObject;
    }

    public void setPooledObject(PooledObject pooledObject) 
    {
        this.pooledObject = pooledObject;
    }

    public void callPooledObjectMethod()
    {
        pooledObject.foo();
    }
}