Inject Prototype Bean into Singleton Bean in Spring – Lookup Method Injection

Lookup Method

In this tutorial I am going to show you how lookup method injection works in Spring framework. You may face a situation where you need to inject prototype scoped bean into singleton scoped bean. For singleton scoped bean a new instance or object is created and the same is returned each time it is injected or looked up. But for prototype scoped bean a new object or instance is created each time it is injected or looked up.

Prototype scoped bean is created at the time of usage. So prototype-scoped beans are not initialized on startup, unless something else has a reference to them. When you find that an instance of prototype scoped bean gets created during application startup, then you must have a reference from a singleton bean to a protoype bean, and the initialization of the singleton bean is triggering the creation of the prototype bean. So when you need stateful beans, there is a strong need to have prototype scope or when you don’t want to cache any values in beans.

The singleton scoped bean is created at the time of, by default, startup of the application.

When you use prototype bean as a dependency into the singleton scoped bean, be aware that dependencies are resolved at instantiation time. Thus if you inject a prototype scoped bean into a singleton scoped bean, a new prototype bean is instantiated or created and then injected into the singleton bean. In this scenario only one instance of the prototype bean is created and supplied to the singleton scoped bean. No matter how many times you call the singleton bean, the same instance of the prototype bean is supplied through singleton bean.

However, if you want the singleton scoped bean to supply a new instance of the prototype scoped bean on each call to singleton bean at runtime. You cannot inject a prototype scoped bean into singleton bean, because the injection occurs only once, when the Spring container is instantiating the singleton bean and resolving and injecting its dependencies. Here is the scenario where Spring framework’s lookup method injection comes to play.

Lookup method injection is the ability of the container to override methods on container managed beans, to return the lookup result for another named bean in the container. The lookup typically involves a prototype bean when you need to inject prototype bean into singleton bean. The Spring Framework implements this method injection by using bytecode generation from the CGLIB library to generate dynamically a subclass that overrides the method.

For this dynamic subclassing to work, you must have the CGLIB jar(s) in your classpath. The class that the Spring container will subclass cannot be final, and the method to be overridden cannot be final either. Also, testing a class that has an abstract method requires you to subclass the class yourself and to supply a stub implementation of the abstract method. Finally, objects that have been the target of method injection cannot be serialized.

Let’s understand using an example.

Prerequisites

Java 8/19, Spring Boot 2.4.4/3.1.1, Maven 3.6.3/3.8.5

Project Setup

Now create a maven based project in your favorite IDE or tool. The name of the project is spring-lookup-method-injection.

The maven based build file pom.xml can be given as below:

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.roytuts</groupId>
	<artifactId>spring-lookup-method-injection</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<maven.compiler.source>19</maven.compiler.source>
		<maven.compiler.target>19</maven.compiler.target>
	</properties>
	
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>3.1.1</version>
	</parent>
	
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
		</dependency>
	</dependencies>
	
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>

Injecting Prototype Bean into Singleton Bean

Now I am going to show you what is the problem with simply injecting prototype bean into singleton bean.

Let’s create two bean classes:

Class – BeanB

The scope of BeanB is prototype and here I am using annotation based configuration for declaring bean’s scope.

@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class BeanB {

	public String greetB() {
		return "Greeting from B";
	}

}

Now I am going to inject this bean into BeanA.

Class – BeanA

The BeanA is singleton scoped bean. If you do not specify the scope then the default scope of the bean will be singleton.

@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
public class BeanA {

	@Autowired
	private BeanB beanB;

	public void greetA() {
		System.out.println("beanB: " + beanB);
		System.out.println("Msg: " + beanB.greetB());
	}

}

Spring Boot Main Class

In Spring boot application you generally create a class with main method to start the application.

@SpringBootApplication
public class SpringLookupMethodInjectionApp implements CommandLineRunner {

	@Autowired
	private BeanA beanA;

	public static void main(String[] args) {
		SpringApplication.run(SpringLookupMethodInjectionApp.class, args);
	}

	@Override
	public void run(String... args) throws Exception {
		beanA.greetA();
		beanA.greetA();
	}

}

In the above class inside the run() method I have called greetA() method twice to check whether the BeanB instances are same or different on each call of greetA() method.

Running the above class will give you the following output:

beanB: com.roytuts.spring.lookup.method.injection.BeanB@48c35007
Msg: Greeting from B
beanB: com.roytuts.spring.lookup.method.injection.BeanB@48c35007
Msg: Greeting from B

From the above output it is clear that the single bean instance of the BeanB is returned when injected through singleton bean despite of the scope prototype.

Lookup Method Injection

Now I am going to show you how to use lookup method injection to have separate instance of the BeanB for each call or request.

I am going to use annotation @Lookup for lookup method injection. You need to use @Lookup annotation on the method. The method has the return type BeanB and actually this method will not return instance of BeanB. It will return null and Spring container will provide the instance of BeanB. Spring uses CGLIB library to dynamically override this method to provide implementation and returns instance on each request to this bean.

Now update the BeanA class as follows:

@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
public class BeanA {
	
	public void greetA() {
		System.out.println("beanB: " + beanB());
		System.out.println("Msg: " + beanB().greetB());
	}

	@Lookup
	public BeanB beanB() {
		return null;
	}

}

As you see in the above class, I have removed the @Autowired for BeanB and using the method beanB() with @Lookup annotation.

Now running the application you will see the following output. Now you can see that the instances of BeanB are different.

beanB: com.roytuts.spring.lookup.method.injection.BeanB@bcec031
Msg: Greeting from B
beanB: com.roytuts.spring.lookup.method.injection.BeanB@21005f6c
Msg: Greeting from B

While you are using lookup method injection in Spring application, you need to make sure that your application meets the following conditions:

  • Bean class must not be final
  • Method with @Lookup annotation must not be private, static or final

That’s all about how lookup method injection works in Spring application.

Source Code

Download

Leave a Reply

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