In this post, we will see how to inject prototype bean scope into Singleton Instance in Spring. This is one of the most asked Spring interview questions.
Table of Contents
Problem
When you inject prototype bean to singleton bean, prototype bean still behave like a singleton bean.
Let’s understand this with the help of example.
1. Create a simple java maven project.
2. Maven dependency
put spring and cglib maven dependency in pom.xml.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <!-- cglib required for @configuration annotation --> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.0</version> </dependency> </dependencies> <properties> <spring.version>4.2.1.RELEASE</spring.version> </properties> |
So your pom.xml will look like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
<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>org.arpit.java2blog</groupId> <artifactId>SpringHelloWorldJavaBasedConfiguration</artifactId> <version>0.0.1-SNAPSHOT</version> <name>SpringHelloWorldJavaBasedConfiguration</name> <description>It provides hello world program for java based config </description> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <!-- cglib required for @configuration annotation --> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.0</version> </dependency> </dependencies> <properties> <spring.version>4.2.1.RELEASE</spring.version> </properties> </project> |
3. Create Bean class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
package org.arpit.java2blog; import org.springframework.beans.factory.annotation.Autowired; public class X { @Autowired Y y; public Y getY() { return y; } public void setY(Y y) { this.y = y; } } |
1 2 3 4 5 6 7 8 9 10 11 12 |
package org.arpit.java2blog; public class Y { public void methodY() { System.out.println("In method Y"); } } |
4. ApplicationContext.xml
Create ApplicationContext.xml in src/main/resources as below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <bean id="x" class="org.arpit.java2blog.X"> </bean> <bean id="y" class="org.arpit.java2blog.Y" scope="prototype"> </bean> <context:annotation-config/> </beans> |
We have just declared two bean x and y here.
5. Create InjectPrototypeToSingletonMain.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
package org.arpit.java2blog; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * Injecting prototype dependencies into singleton * */ public class InjectPrototypeToSingletonMain { public static void main( String[] args ) { ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("ApplicationContext.xml"); X firstX = (X) ac.getBean("x"); Y firstY = firstX.getY(); X secondX = (X) ac.getBean("x"); Y secondY = secondX.getY(); System.out.println("do two prototype beans of type Y point to same object: "+firstY.equals(secondY)); ac.close(); } } |
6. Run it
When you run above program, you will get below output.
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@31cefde0: startup date [Fri Jun 15 13:32:58 IST 2018]; root of context hierarchy
Jun 15, 2018 1:32:58 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [ApplicationContext.xml] do two prototype bean point to same object: true
Jun 15, 2018 1:32:59 PM org.springframework.context.support.ClassPathXmlApplicationContext doClose
INFO: Closing org.springframework.context.support.ClassPathXmlApplicationContext@31cefde0: startup date [Fri Jun 15 13:32:58 IST 2018]; root of context hierarchy
As you can note here, even though we have declared bean Y as prototype, still it is behaving like a singleton bean only.
Solution
There are many ways to solve this problem.
Using Method injection
Update X.java as below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
package org.arpit.java2blog; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Lookup; public class X { @Autowired Y y; @Lookup public Y getY() { return y; } public void setY(Y y) { this.y = y; } } |
You can use method injection to solve this problem.Simply use @Lookup with getY() method, it will return new instance each time.
Spring will use CGLIB to generate dynamically a subclass that overrides the method getY(). It then registers the bean into the application context. Whenever we request for getY() method, it will return new instance of Class Y.
There are two options here, either create dummy method or mark method as abstract. When you mark method as abstract, then you will have to declare class abstract as well, hence we have used dummy method in this scenario.
Run InjectPrototypeToSingletonMain again
You will get below output:
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@31cefde0: startup date [Fri Jun 15 13:51:38 IST 2018]; root of context hierarchy
Jun 15, 2018 1:51:38 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [ApplicationContext.xml] do two prototype beans of type Y point to same object: false
Jun 15, 2018 1:51:38 PM org.springframework.context.support.ClassPathXmlApplicationContext doClose
INFO: Closing org.springframework.context.support.ClassPathXmlApplicationContext@31cefde0: startup date [Fri Jun 15 13:51:38 IST 2018]; root of context hierarchy
As you can note now, we are getting two different instance of Y now.
Using ObjectFactory
You can use ObjectFactory to get new object each time getY() method is called.
Update class A.java as below
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
package org.arpit.java2blog; import org.springframework.beans.factory.ObjectFactory; import org.springframework.beans.factory.annotation.Autowired; public class X { @Autowired private ObjectFactory<Y> prototypeBeanObjectFactory; Y y; public Y getY() { return prototypeBeanObjectFactory.getObject(); } public void setY(Y y) { this.y = y; } } |
Run InjectPrototypeToSingletonMain again
You will get below output:
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@31cefde0: startup date [Fri Jun 15 14:55:56 IST 2018]; root of context hierarchy
Jun 15, 2018 2:55:57 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [ApplicationContext.xml] do two prototype beans of type Y point to same object: false
Jun 15, 2018 2:55:58 PM org.springframework.context.support.ClassPathXmlApplicationContext doClose
INFO: Closing org.springframework.context.support.ClassPathXmlApplicationContext@31cefde0: startup date [Fri Jun 15 14:55:56 IST 2018]; root of context hierarchy
Using ApplicationContextAware
You can return new bean using application context wheneven getY() method is getting called.
Update class A.java as below
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
package org.arpit.java2blog; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; public class X implements ApplicationContextAware{ private ApplicationContext applicationContext; Y y; public Y getY() { return applicationContext.getBean(Y.class); } public void setY(Y y) { this.y = y; } @Override public void setApplicationContext(ApplicationContext arg0) throws BeansException { this.applicationContext=arg0; } } |
Run InjectPrototypeToSingletonMain again
You will get below output:
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@31cefde0: startup date [Fri Jun 15 14:58:59 IST 2018]; root of context hierarchy
Jun 15, 2018 2:58:59 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [ApplicationContext.xml] do two prototype beans of type Y point to same object: false
Jun 15, 2018 2:59:00 PM org.springframework.context.support.ClassPathXmlApplicationContext doClose
INFO: Closing org.springframework.context.support.ClassPathXmlApplicationContext@31cefde0: startup date [Fri Jun 15 14:58:59 IST 2018]; root of context hierarchy
That’s all about Injecting Prototype bean into a Singleton bean in Spring.
For more questions, refer Spring interview questions.