Injecting Prototype bean in a Singleton bean

Please first go through the below link for understanding of bean scopes in Spring framework before you delve further into this article.

All About Spring Beans

What is a singleton bean?

When a bean is a singleton, only one shared instance of the bean will be managed, and all requests for beans with an id or ids matching that bean definition will result in that one specific bean instance being returned by the Spring container.

What is a prototype bean?

prototype bean – results in the creation of a new bean instance every time a request for that specific bean is made (that is, it is injected into another bean or it is requested via a programmatic getBean() method call on the container).

Let’s see what happens when we inject a prototype bean into a singleton bean.

PrototypeBean
package com.spring.beans;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
@Scope(value=”prototype”)
public class PrototypeBean {
public PrototypeBean() {
System.out.println(“PrototypeBean created”);
}
}

SingletonBean

package com.spring.beans;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
@Scope(value=”singleton”)
public class SingletonBean {
public SingletonBean() {
System.out.println(“SingletonBean created”);
}
@Autowired
 private PrototypeBean prototypeBean;
public PrototypeBean getPrototypeBean() {
return prototypeBean;
}
}
Application
package com.spring.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import com.spring.beans.SingletonBean;
@SpringBootApplication(scanBasePackages=”com.spring.beans”)
public class Application implements CommandLineRunner{
@Autowired
private ApplicationContext context;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Override
public void run(String… args) throws Exception {
SingletonBean singletonBean1 = context.getBean(SingletonBean.class);
SingletonBean singletonBean2 = context.getBean(SingletonBean.class);
if(singletonBean1.getPrototypeBean() == singletonBean2.getPrototypeBean()) {
System.out.println(“Only one instance created for PrototypeBean”);
}
System.exit(1);
}
}

Output

SingletonBean created
PrototypeBean created
Only one instance created for PrototypeBean

In the above example, we are fetching SingletonBean twice from the ApplicationContext. You may have expected a new PrototypeBean for each fetch of SingletonBean. But in reality, the above just creates a Single instance of PrototypeBean.

Why is it so?

SingletonBean singletonBean1 = context.getBean(SingletonBean.class);

When the above code is executed.

ApplicationContext first checks if there is any existing SingletonBean in the context.

It finds none, so it does the below.

ApplicationContext first creates an instance of the PrototypeBean. Then it creates an instance of the SingletonBean and injects the already created PrototypeBean instance into the SingletonBean instance.

So, we have one instance each of SingletonBean and PrototypeBean.

Let’s see what happens when the below code is executed.

SingletonBean singletonBean2 = context.getBean(SingletonBean.class);

SingletonBean being scoped as “singleton”, only one instance is created by the Spring Container. So the context returns the earlier created SingletonBean instance. So, no new instance of SingletonBean and PrototypeBean is created.

How to resolve this problem?

We can resolve this problem using the following ways.

  1. Injecting ApplicationContext in SingletonBean and get PrototypeBean from ApplicationContext in getter of PrototypeBean.
  2. Using Lookup Method Injection
  3. Using ObjectFactory
  4. Using Scoped Proxy

Let’s take a look into each one of the above.

Injecting ApplicationContext in SingletonBean and get PrototypeBean from ApplicationContext in getter of PrototypeBean.

Every time the getPrototypeBean() method is called, a new instance of PrototypeBean will be returned from the ApplicationContext.

SingletonBean
package com.spring.beans;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
@Scope(value=”singleton”)
public class SingletonBean {
public SingletonBean() {
System.out.println(“SingletonBean created”);
}
@Autowired
private ApplicationContext context;
public PrototypeBean getPrototypeBean() {
return context.getBean(PrototypeBean.class);
}
}
Application
package com.spring.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import com.spring.beans.SingletonBean;
@SpringBootApplication(scanBasePackages=”com.spring.beans”)
public class Application implements CommandLineRunner{
@Autowired
private ApplicationContext context;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Override
public void run(String… args) throws Exception {
SingletonBean singletonBean1 = context.getBean(SingletonBean.class);
SingletonBean singletonBean2 = context.getBean(SingletonBean.class);
if(singletonBean1.getPrototypeBean() == singletonBean2.getPrototypeBean()) {
System.out.println(“Only one instance created for PrototypeBean”);
}else {
System.out.println(“Two instances created for PrototypeBean”);
}
System.exit(1);
}
}
Output

SingletonBean created
PrototypeBean created

PrototypeBean created
Two instances created for PrototypeBean

Using Lookup Method Injection

The Spring Framework implements method injection by using CGLIB library to generate dynamically a subclass that overrides the method. So for the method to be overridden, we have to provide a dummy implementation for it.

@Lookup
    public PrototypeBean getPrototypeBean() {
        return null;
    }

Whenever we define a bean with lookup methods, Spring creates a subclass of the bean and overrides those methods which are marked as lookup-methods. And this subclassed bean gets registered into the context. The subclass delegates all the non-lookup methods to the original class. For the lookup methods, it overrides the implementation. So in our example, when getPrototypeBean() is called, it returns a new PrototypeBean instance.

SingletonBean

package com.spring.beans;
import org.springframework.beans.factory.annotation.Lookup;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
@Scope(value=”singleton”)
public class SingletonBean {
public SingletonBean() {
System.out.println(“SingletonBean created”);
}
@Lookup
    public PrototypeBean getPrototypeBean() {
        return null;
    }
}
Application
package com.spring.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import com.spring.beans.SingletonBean;
@SpringBootApplication(scanBasePackages=”com.spring.beans”)
public class Application implements CommandLineRunner{
@Autowired
private ApplicationContext context;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Override
public void run(String… args) throws Exception {
SingletonBean singletonBean1 = context.getBean(SingletonBean.class);
SingletonBean singletonBean2 = context.getBean(SingletonBean.class);
if(singletonBean1.getPrototypeBean() == singletonBean2.getPrototypeBean()) {
System.out.println(“Only one instance created for PrototypeBean”);
}else {
System.out.println(“Two instances created for PrototypeBean”);
}
System.exit(1);
}
}
Output

SingletonBean created
PrototypeBean created

PrototypeBean created
Two instances created for PrototypeBean

Using ObjectFactory

Spring provides the ObjectFactory<T> interface to produce on demand objects of the given type.

Whenever we invoke the getPrototypeBean() method, getObject() of the ObjectFactory returns a new instance of PrototypeBean.

SingletonBean

package com.spring.beans;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
@Scope(value = “singleton”)
public class SingletonBean {
public SingletonBean() {
System.out.println(“SingletonBean created”);
}
@Autowired
private ObjectFactory<PrototypeBean> prototypeBeanObjectFactory;
public PrototypeBean getPrototypeBean() {
return prototypeBeanObjectFactory.getObject();
}
}
Application
package com.spring.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import com.spring.beans.SingletonBean;
@SpringBootApplication(scanBasePackages=”com.spring.beans”)
public class Application implements CommandLineRunner{
@Autowired
private ApplicationContext context;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Override
public void run(String… args) throws Exception {
SingletonBean singletonBean1 = context.getBean(SingletonBean.class);
SingletonBean singletonBean2 = context.getBean(SingletonBean.class);
if(singletonBean1.getPrototypeBean() == singletonBean2.getPrototypeBean()) {
System.out.println(“Only one instance created for PrototypeBean”);
}else {
System.out.println(“Two instances created for PrototypeBean”);
}
System.exit(1);
}
}
Output

SingletonBean created
PrototypeBean created

PrototypeBean created
Two instances created for PrototypeBean

Using Scoped Proxy

The Spring container injects the proxy object for PrototypeBean into the SingletonBean.

Invocation  of getPrototypeBean() in SingletonBean actually returns a proxy Object.

Each time the method on the proxy object is called, the proxy decides itself whether to create a new instance of the real object or reuse the existing one based on the bean scope.

In our case, when we call getPrototypeBean().toString(), then proxy object creates a new instance of PrototypeBean and invokes toString on it.

By default, Spring uses CGLIB library to directly subclass the objects. To avoid CGLIB usage, we can configure the proxy mode with ScopedProxyMode.INTERFACES, to use the JDK dynamic proxy instead.

PrototypeBean

package com.spring.beans;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.stereotype.Component;
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class PrototypeBean {
@Override
public String toString() {
return “PrototypeBean [createdTime=” + System.nanoTime()+”]”;
}
public PrototypeBean() {
System.out.println(“PrototypeBean created”);
}
}
SingletonBean
package com.spring.beans;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
@Scope(value = “singleton”)
public class SingletonBean {
public SingletonBean() {
System.out.println(“SingletonBean created”);
}
@Autowired
private PrototypeBean prototypeBean;
public PrototypeBean getPrototypeBean() {
return prototypeBean;
}
}
Application
package com.spring.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import com.spring.beans.SingletonBean;
@SpringBootApplication(scanBasePackages=”com.spring.beans”)
public class Application implements CommandLineRunner{
@Autowired
private ApplicationContext context;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Override
public void run(String… args) throws Exception {
SingletonBean singletonBean1 = context.getBean(SingletonBean.class);
SingletonBean singletonBean2 = context.getBean(SingletonBean.class);
String singletonBean1Str = singletonBean1.getPrototypeBean().toString();
String singletonBean2Str = singletonBean2.getPrototypeBean().toString();
System.out.println(“singletonBean1:”+singletonBean1Str);
System.out.println(“singletonBean2:”+singletonBean2Str);
if(singletonBean1Str.equalsIgnoreCase(singletonBean2Str)) {
System.out.println(“Only one instance created for PrototypeBean”);
}else {
System.out.println(“Two instances created for PrototypeBean”);
}
System.exit(1);
}
}
Output
SingletonBean created
PrototypeBean created
PrototypeBean created
singletonBean1:PrototypeBean [createdTime=114399774817583]
singletonBean2:PrototypeBean [createdTime=114399775180603]
Two instances created for PrototypeBean

Related Articles:

Building REST API using Spring Boot

Spring Conditional Annotations

Leave a Reply

Your email address will not be published. Required fields are marked *