Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 13

Bean Scopes: Singleton & Prototype

Recommended resources

Hello! Welcome back to your course on backend development with Java and Spring.

In our last lesson, we explored how to wire beans together using constructor and setter-based dependency injection. We can now create a system where components like a UserService can collaborate with a UserRepository.

This brings up a fundamental question: when Spring injects a UserRepository into multiple services, are they all receiving the exact same repository object, or does each get a brand new one?

Today, we will answer that question by diving into bean scopes. This lesson directly addresses the learning outcome: Define and use core bean scopes (singleton and prototype). Understanding scopes is essential for controlling the lifecycle of your beans, managing application state, and ensuring your application is both efficient and correct.

1. What Are Spring Bean Scopes?

A bean's scope determines how many instances of that bean are created and how long they live within the Spring IoC container. Spring offers several scopes, but for now, we will focus on the two most fundamental ones: singleton and prototype. The others (request, session, application, and websocket) are specific to web applications and we may revisit them later in the course.

To start, let's get a high-level overview of what bean scopes are.

Quick Guide to Spring Bean Scopes

This article, 'Quick Guide to Spring Bean Scopes' from Baeldung, provides a concise introduction to the concept. It lists the available scopes and sets the stage for our discussion.

Please read Section 1, 'Overview'. This will give you a quick definition of bean scopes and list the six types available in Spring.


2. The Singleton Scope: One Bean to Rule Them All

The singleton scope is the default behavior in Spring. When a bean is defined as a singleton, the Spring IoC container creates only one instance of that bean. This single instance is stored in a cache, and every time any other bean requests it for injection, the container returns this same, shared instance.

Spring Bean Scopes: Singleton vs. Prototype
This image visually contrasts the Singleton and Prototype scopes. On the left, you can see that a Singleton bean results in a single, shared instance for all dependent components.

Characteristics of a Singleton Bean:

  • One Instance: Only one object instance per Spring container.
  • Default Behavior: If you don't specify a scope, Spring makes it a singleton.
  • Eager Initialization: By default, Spring creates all singleton beans when the application context starts up, not when they are first requested.
  • Ideal Use Case: Stateless beans. Components like services, repositories, and configurations are perfect candidates for singletons because they don't hold any state specific to one user or one operation. Sharing a single instance is highly memory-efficient.

Let's watch a practical demonstration.

Spring & Spring Boot Annotations Series - #8 - @Scope Annotation | Singleton and Prototype Scopes

The video '@Scope Annotation | Singleton and Prototype Scopes' from Java Guides provides a clear, hands-on demonstration of how scopes work. We'll start by focusing on the singleton part.

Please watch from 00:40 to 01:05 to understand the definition of singleton scope. Then, watch from 01:52 to 10:41. The video will walk you through creating both singleton and prototype beans. Pay close attention to how he retrieves the singleton bean multiple times from the application context and prints its hash code to prove that it's the exact same object every time. He also demonstrates that singleton is the default scope by commenting out the @Scope annotation.

How to Define a Singleton Scope

As the video showed, you can explicitly define a singleton scope using the @Scope annotation. While this isn't necessary (since it's the default), it can make your code more explicit.

import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;

@Service
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON) // Or simply @Scope("singleton")
public class ProductService {
    // ... stateless business logic
}

Using the constant ConfigurableBeanFactory.SCOPE_SINGLETON is generally preferred over the string "singleton" as it prevents typos.


3. The Prototype Scope: A New Bean Every Time

In contrast to a singleton, a bean with the prototype scope results in a new instance being created every single time it is requested from the container. This applies whether you're asking for it directly via applicationContext.getBean() or when it's injected into another bean.

Characteristics of a Prototype Bean:

  • Many Instances: A new object is created for each request.
  • Lazy Initialization: Prototype beans are only created when they are actually requested.
  • Lifecycle Management: Spring creates, configures, and assembles the prototype object and then hands it over to the client. After that, the container does not manage the rest of its lifecycle. This means if your prototype bean uses resources that need to be released (like a file handle), your application is responsible for that cleanup.
  • Ideal Use Case: Stateful beans. Any bean that needs to hold data specific to a single task or user should be a prototype. A classic example is a shopping cart object in an e-commerce application.

Now, let's look at the prototype part of the same video you just watched, which directly contrasts it with the singleton behavior.

If you followed the previous video instruction, you've already seen the prototype in action. The key takeaway was that when the PrototypeBean was requested three times, three different hash codes were printed, confirming that three distinct objects were created.

How to Define a Prototype Scope

To override the default singleton behavior, you must use the @Scope annotation.

import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) // Or @Scope("prototype")
public class ShoppingCart {
    // ... stateful logic to hold items for one user
}

Here, using @Scope is mandatory to get the prototype behavior.


4. Singleton vs. Prototype: A Conceptual Summary

You've seen the practical difference, but it's also useful to understand the conceptual model behind it.

Spring Tutorial 11 - Understanding Bean Scopes

The classic video 'Understanding Bean Scopes' from Java Brains provides an excellent conceptual explanation, particularly regarding when beans are created. Although it uses older XML-based configuration, the core principles are timeless and directly support your goal of a deep theoretical understanding.

Watch from 03:48 to 08:03. Focus on the explanation of when singleton beans are created (at startup) versus when prototype beans are created (on request). Don't worry about the XML syntax; just absorb the concepts.

To summarize the key differences:

Feature Singleton Prototype
Instance Count One per Spring container One new instance per request/injection
Default Scope Yes No
Creation Time Eagerly (at application startup, by default) Lazily (on-demand when requested)
State Management Ideal for stateless beans (e.g., Repositories, Services) Ideal for stateful beans (e.g., Shopping Cart)
Lifecycle Fully managed by Spring (creation and destruction) Creation managed by Spring, but destruction is not
Test your understanding!

You are building an online application with the following components:

  1. A DatabaseConnectionPool bean that manages a pool of connections to your MySQL database. The pool should be initialized once and shared across the entire application to be efficient.
  2. A UserSession bean that stores information about the currently logged-in user, like their username and login time.

What scope would you assign to DatabaseConnectionPool and UserSession? Explain your reasoning.

Show answer
  1. DatabaseConnectionPool: This should be a singleton. A connection pool is a heavy, resource-intensive object that is designed to be created once and shared by all parts of the application that need to talk to the database. Creating a new pool for every request would be extremely inefficient and defeat its purpose.

  2. UserSession: This should be a prototype. Each user's session data is unique and must be isolated from other users. If UserSession were a singleton, all users would share the same session object, leading to massive data corruption and security issues (e.g., one user seeing another user's data). A new UserSession instance is needed for each user. (In a real web application, you might use the session scope, but prototype is the correct choice given the options we've learned).


Conclusion

Congratulations! You now understand one of the most fundamental concepts of the Spring framework. Mastering bean scopes allows you to design applications that are not only correct but also efficient in their use of memory and resources.

Key Takeaways:

  • A bean's scope controls its lifecycle and the number of instances created.
  • Singleton is the default scope. It creates one shared instance per container and is ideal for stateless components.
  • Prototype creates a new, unique instance every time a bean is requested. It is necessary for stateful components that must not share data.
  • You can explicitly set a bean's scope using the @Scope annotation.

What's Next?

So far, our beans have been self-contained. But what if a service needs a value that might change between development and production environments, like a server URL or a password? Hard-coding these values is not a good practice.

In our next lesson, we will learn how to externalize configuration using application.properties files and inject these values into our beans with the @Value annotation. This will make your applications far more flexible and easier to manage.