In this post, we will discuss two different types of Spring beans, namely singleton and prototype beans. We will also discuss when is it preferred to use prototype beans and when to use singleton beans.
Singleton beans
Most beans that you will use or encounter are singleton beans. Singleton beans are initialized only once. Either when the application (context) starts up or manually via lazy loading.
Singleton beans as in the name are singleton. This means that once a singleton bean is initialized, the same instance will be reused throughout the application context. This also means that every-time the bean is requested from the application context, the same instance of the bean will be provided.
Singleton beans are ideal for resource intensive components, such as database management services where database connections are opened. Or beans that require resources to be acquired on startup such as system memory or other physical or network resources. For example, you do not want to start a new database connection everytime you need to run a database query. For this, a singleton bean is ideal as the same connection will be reused.
Let practice by creating a singleton bean class.
package com.nullbeans.accounting.services; public class SingletonServiceBean { private static int instancesCreated = 0; //This constructor will be called everytime a new bean instance is created public SingletonServiceBean(){ instancesCreated++; } public static int getInstancesCreated(){ return SingletonServiceBean.instancesCreated; } }
This bean is a regular Spring bean. For testing purposes we added the static instancesCreated counter in order to test the number of times the constructor is called. This way we could tell when a new instance of the bean is created. Now, let us move on to the bean configuration.
public class SingletonVsPrototypeBeanConfig { @Bean public SingletonServiceBean singletonServiceBean(){ return new SingletonServiceBean(); }
Spring beans configured inside Java configuration classes are singleton by default. Therefore, it is not required to explicitly set the bean type.
In order to set the Spring bean type to Singleton or Prototype, you will need to define the bean scope. This is done in Java config using the @Scope annotation.
@Bean @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON) public SingletonServiceBean singletonServiceBean(){ return new SingletonServiceBean(); }
Please note that in the previous code snippet, we set the bean scope only for demonstration purposes. Since a bean is singleton by default, it was not required to set the scope of that bean.
Prototype beans
Prototype beans, like singleton beans are also Spring beans. Unlike singleton beans, Spring will provide you with a new instance of the prototype bean every-time the bean is requested. This means that a new instance of the prototype bean class will be created.
package com.nullbeans.accounting.services; public class PrototypeServiceBean { private static int instancesCreated = 0; //This constructor will be called everytime a new bean instance is created public PrototypeServiceBean(){ instancesCreated++; } public static int getInstancesCreated(){ return PrototypeServiceBean.instancesCreated; } }
As you can see, programatically, a prototype bean class looks exactly the same as a singleton. Let us configure the prototype bean in our Java config.
@Bean @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) public PrototypeServiceBean prototypeServiceBean(){ return new PrototypeServiceBean(); }
In order to mark a bean as a prototype, we will need to use the @Scope annotation with the value ConfigurableBeanFactory.SCOPE_PROTOTYPE.
Differences between Prototype beans and Singleton beans
So how does Spring behave when handling prototype and singleton beans and which differences in behavior can we expect while doing various Spring operations?
Autowiring
When you autowire a bean, you ask Spring for an instance of the bean from the application context. If you autowire a singleton bean, Spring looks for an existing instance inside the application context and provides it to you. If you autowire the bean in multiple places, Spring will still provide you with the same instance.
When you autowire a prototype bean, Spring will initialize a new instance of the bean. If you autowire the bean in multiple places, then Spring will create a new instance for every place you autowire the bean.
Let us demonstrate this behavior by creating a test bean and a spring test where we autowire our test beans. Let us start with our test service class.
public class TestServiceBean { private static final Logger logger = LoggerFactory.getLogger(TestServiceBean.class); @Autowired private PrototypeServiceBean prototypeServiceBean; @Autowired private SingletonServiceBean singletonServiceBean; }
Now, let us use this service bean inside a Spring boot test. In our spring boot test, we will request our test bean and we will request instances of our singleton and prototype beans as well.
This means that each bean will be autowired twice. Once inside the test service class and a second time inside the test class.
@RunWith(SpringRunner.class) @Import(SingletonVsPrototypeBeanConfig.class) @SpringBootTest public class SingletonVsPrototypeTest { private static final org.slf4j.Logger logger = LoggerFactory.getLogger(SingletonVsPrototypeTest.class); @Autowired private TestServiceBean testServiceBean; @Autowired private PrototypeServiceBean prototypeServiceBean; @Autowired private SingletonServiceBean singletonServiceBean; @Test public void testInstancesCreated(){ logger.info("Instances of PrototypeServiceBean created so far is {}", PrototypeServiceBean.getInstancesCreated()); logger.info("Instances of singletonServiceBean created so far is {}", SingletonServiceBean.getInstancesCreated()); } }
And the result of this program would be:
Instances of PrototypeServiceBean created so far is 2 Instances of singletonServiceBean created so far is 1
As expected, the Prototype bean’s constructor has been called twice. Once for the instance autowired inside our test bean and the second is for the autowired instance inside the test class. In contrast, the singleton bean’s constructor is called only once. This is because Spring reuses the same instance.
Requesting a bean instance manually
Requesting a bean instance manually yields the same result as autowiring. Every time you request a singleton bean from the application context, you obtain the same instance. However, if you request a prototype bean, then a new instance is created for you with each request. Let us modify our test bean to add the following method.
public class TestServiceBean { @Autowired private ApplicationContext applicationContext; @Autowired private PrototypeServiceBean prototypeServiceBean; @Autowired private SingletonServiceBean singletonServiceBean; public void testInstances(){ PrototypeServiceBean localInstancePrototype = applicationContext.getBean(PrototypeServiceBean.class); SingletonServiceBean localInstanceSingleton = applicationContext.getBean(SingletonServiceBean.class); } }
Now, let us call our testInstances method in our test as follows.
@Test public void testInstancesCreated(){ testServiceBean.testInstances(); logger.info("Instances of PrototypeServiceBean created so far is {}", PrototypeServiceBean.getInstancesCreated()); logger.info("Instances of singletonServiceBean created so far is {}", SingletonServiceBean.getInstancesCreated()); }
And the result will look as follows.
Instances of PrototypeServiceBean created so far is 3 Instances of singletonServiceBean created so far is 1
Notice now that the prototype bean is created 3 times. Once inside our test bean via the @Autowired annotation, once inside the testServiceBean.testInstances() method, and once more via the @Autowired annotation inside our test. The singleton bean had only one instance of it created.
Shutting down the beans
There is a fundamental difference between singleton and prototype beans when it comes to managing the shutdown phase of the beans’ lifecycle. The difference is that Spring will clean up singleton beans and destroy them once the containing application context is destroyed. That means that a singleton bean will remain in your JVM memory unless the application context is shutdown or if the bean is manually destroyed.
On the other hand, prototype beans will not be cleaned up by Spring and the bean instances will be left up to the garbage collector for cleaning. Let us explore this with an example. Let us define the destroy method inside both our singleton and our prototype bean.
public class SingletonServiceBean { private static final Logger log = LoggerFactory.getLogger(SingletonServiceBean.class); ..... public void destroy(){ log.info("Shutting down Singleton bean"); } }
public class PrototypeServiceBean { private static final Logger log = LoggerFactory.getLogger(PrototypeServiceBean.class); ...... public void destroy(){ log.info("Shutting down Prototype bean"); } }
And let us mark the destroy methods in our Java configuration.
@Bean(destroyMethod = "destroy") @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) public PrototypeServiceBean prototypeServiceBean(){ return new PrototypeServiceBean(); } @Bean(destroyMethod = "destroy") @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON) public SingletonServiceBean singletonServiceBean(){ return new SingletonServiceBean(); }
Finally, let us run our test again and analyse the logged statements.
[main] Instances of PrototypeServiceBean created so far is 3 [main] Instances of singletonServiceBean created so far is 1 [Thread-3] Shutting down Singleton bean
Notice that the destroy method of the singleton bean was called, but not the one of the prototype bean. Therefore it is important to manually clean up any resources such as streams and buffers inside prototype beans before finishing your use case. This can be done by manually calling the bean’s destroy method at the end of the bean’s usage.
When to use singleton beans and when to use prototype beans
We have compiled below a list of reasons or usages for both types of beans. Let us start with singleton beans:
- Useful as “stateless” beans.
- For beans that require expensive resources on start up such as database connections.
- For beans that are used for caching and sharing data throughout your application.
- When an orderly and controlled shutdown is required.
The following scenarios could be more appropriate for prototype beans:
- Stateful beans that fulfill a purpose of a single call or a single use.
- For entities where Spring support is required. For example, when having a list of “Accounts”, where each account holds different data. Each account can be a prototype bean. You might need such design if you need to use functionalities from Spring inside each Account instance such as performing calls to other Spring beans. Sometimes you might need such unusual workarounds.
Summary
In this tutorial, we discussed how to define a prototype bean and a singleton bean. We discussed how spring handles these beans differently at creation time and at shutdown. We also discussed some reasons why one would prefer a scope over the other. Below is a table summarizing the differences discussed in this tutorial.
Singleton bean | Prototype bean |
Default Spring bean type. Can be explicitly configured by:@Scope (ConfigurableBeanFactory .SCOPE_SINGLETON) | Can be configured via the annotation:@Scope (ConfigurableBeanFactory .SCOPE_PROTOTYPE) |
Only one instance is created. It is reused everywhere. | A new instance is created everytime the bean is requested. |
Destroyed by Spring when the application context shuts down. The bean’s destroy method is called. | Not destroyed by Spring. The destroy method is not called. Cleaned up is done by the garbage collector. The user is responsible for cleaning up any resources inside the bean. |
Preferred scope for beans with heavy startup resources. | Preferred scope for beans with lighter startup resources. |
Used in stateless situations. | Used for stateful situations. |
If you like this tutorial, then please subscribe and let us know in the comments below.
Leave a Reply
You must be logged in to post a comment.