Spring Prototype scoped bean in a singleton

I am trying to inject a prototype bean in a singleton bean such that every new call to a singleton bean method has a new instance of the prototype bean.

Consider a singleton bean as below:

    @Component
    public class SingletonBean {
       @Autowired 
       private PrototypeBean prototypeBean;

       public void doSomething() {
         prototypeBean.setX(1);
         prototypeBean.display();
       }
    }

I expect that every time the doSomething() method is called, a new PrototypeBean instance is utilized.

Below is the prototype bean:

     @Component 
     @Scope(value="prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
     public class PrototypeBean {
        Integer x;

        void setX(Integer x) {
         this.x = x;
        }

        void display() {
          System.out.println(x);
        }
    }

What seems to be happening is that spring is being overeager in handing over a new instance of PrototypeBean in the doSomething() method. That is, the 2 lines of code in the doSomething() method are creating a new instance of prototypeBean in each line.

And so in the 2nd line - prototypeBean.display() prints NULL.

What is missing in configuration for this injection?


Solution 1:

From Spring documentation:

You do not need to use the <aop:scoped-proxy/> in conjunction with beans that are scoped as singletons or prototypes. If you try to create a scoped proxy for a singleton bean, the BeanCreationException is raised.

It seems the documentation has changed a bit for version 3.2 documentation where you can find this sentence:

You do not need to use the <aop:scoped-proxy/> in conjunction with beans that are scoped as singletons or prototypes.

It seems that its not expected you use a proxied prototype bean, as each time it is requested to the BeanFactory it will create a new instance of it.


In order to have a kind of factory for your prototype bean you could use an ObjectFactory as follows:

@Component
public class SingletonBean {

    @Autowired
    private ObjectFactory<PrototypeBean> prototypeFactory;

    public void doSomething() {
        PrototypeBean prototypeBean = prototypeFactory.getObject();
        prototypeBean.setX(1);
        prototypeBean.display();
    }
}

and your prototype bean would be declared as follows:

@Component 
@Scope(value="prototype")
public class PrototypeBean {
    // ...
}

Solution 2:

Singleton bean is created only once so the prototype bean which is injected also will be created once at the instantiation of singleton bean.The same instance of prototype bean will be used for every request.

If new instance of prototype bean will be created for each request at runtime ,the below method Injection can be used

Example

public class Singleton {
    private Prototype prototype;

    public Singleton(Prototype prototype) {
        this.prototype = prototype;
    }

    public void doSomething() {
         prototype.foo();
    }

    public void doSomethingElse() {
        prototype.bar();
    }
}

public abstract class Singleton {
    protected abstract Prototype createPrototype();

    public void doSomething() {
        createPrototype().foo();
    }

    public void doSomethingElse() {
        createPrototype().bar();
    }
}


<bean id="prototype" class="ch.frankel.blog.Prototype" scope="prototype" />
<bean id="singleton" class="sample.MySingleton">
   <lookup-method name="createPrototype" bean="prototype" />
</bean>

Solution 3:

Right way to achieve it - use lookup method injection and everywhere where you used beans use lookup method invocation (detailed answer)