A Guide to Spring Bean Scopes

In this tutorial, you will learn about Spring Bean Scopes, an important part of the Spring Framework. You will learn what they are, how they work, and when to use them. By the end, you’ll have a clear understanding of Spring Bean Scopes, helping you build better Spring applications.

Introduction to Spring Beans

In the world of Spring Framework, the term “Spring Beans” is quite central. Essentially, a Spring Bean is an object that is instantiated, assembled, and otherwise managed by the Spring IoC (Inversion of Control) container. These beans are created with the configuration metadata that you supply to the container, for instance, in the form of XML definitions or annotations in the source code.

The beans can be thought of as the fundamental building blocks of any Spring application. They are the objects that form the backbone of the application and that are managed by the Spring IoC container. These beans are created with the definitions provided in your project and are used to fulfill various roles within the application, such as service layer classes, data access objects (DAOs), presentation objects like Spring MVC controllers, and even simple objects.

What are Spring Bean Scopes

The Spring Bean Scopes, or in other words, the scope of the beans, dictate the lifecycle and visibility of these beans within the various contexts of an application.

The scope of a Spring Bean defines the boundaries within which the bean exists, the context it is tied to, and how long it lives. In a nutshell, it defines when a new instance of a bean is created and at which point that particular instance will be removed.

Spring offers several scopes, such as:

  • Singleton,
  • Prototype,
  • Request,
  • Session,
  • Application, and
  • Websocket.

Each of these scopes implies a different lifecycle and visibility for the beans they define. The correct understanding and usage of these scopes are crucial for building robust and efficient Spring applications.

In this guide, we will explore these different bean scopes, illustrate their use cases, and provide examples to help you better understand and utilize them in your Spring applications. The goal is to give you a thorough understanding of how these scopes work, so you can design your Spring applications more efficiently and effectively.

Singleton Scope

In the world of Spring Framework, Singleton is the default bean scope. When a bean is defined as Singleton, the Spring IoC container creates exactly one instance of the object defined by that bean definition. This single instance is stored in a cache of such singleton beans, and all subsequent requests and references for that named bean return the cached object. Remember, the Singleton scope is valid only per container, not application-wide. So, if you have multiple Spring containers running, each will have its own Singleton instance.

Use Cases for Singleton Scope

When it comes to using the Singleton scope, the best scenarios usually involve stateless beans. This means beans that don’t store client-specific data. Why is this important? Because in such services as Database Connection Pools, Business Logic Components, and Loggers, it’s unnecessary to maintain multiple instances with their own states. A single, shared instance – a Singleton – is perfectly suited for these cases.

However, there’s a flip side. Suppose your bean needs to hold a different state for each client, something often referred to as a conversational state. In this case, a Singleton might not be the best fit. Other scopes could serve your purpose more effectively.

Singleton Scope Example

Let’s look at a simple example to demonstrate Singleton Scope:

@Configuration
public class AppConfig {

    @Bean
    @Scope("singleton")
    public MySingletonBean myBean() {
        return new MySingletonBean();
    }
}

public class MySingletonBean {
    // class body...
}

In the above code, the myBean bean is declared with Singleton scope. If we request this bean from the container multiple times, we will get the exact same instance each time.

We can test it with the following test method.

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = AppConfig.class)
public class MySingletonBeanTest {

    @Autowired
    private ApplicationContext context;

    @Test
    public void whenSingletonScope_thenSingleInstanceCreated() {
        MySingletonBean firstInstance = context.getBean(MySingletonBean.class);
        MySingletonBean secondInstance = context.getBean(MySingletonBean.class);

        assertSame(firstInstance, secondInstance);
    }
}

In the test, firstInstance and secondInstance are two variables referring to the same MySingletonBean object, because by default the beans in Spring are Singleton-scoped. The assertSame assertion verifies that the two instances are indeed the same.

This simple test verifies that our bean has Singleton scope, as it proves that the Spring IoC container is only creating a single instance of MySingletonBean. You can follow similar steps to create tests for other scopes as well, but you may need to set up additional configuration for certain scopes (like request, session, or websocket).

Best Practices for Singleton Scope

While using Singleton Scope, you should be aware of some best practices:

  1. Statelessness: As much as possible, make your Singleton beans stateless. Stateful Singletons can lead to hard-to-debug issues related to shared state, especially in a multi-threaded environment.
  2. Thread Safety: If your Singleton beans have state, ensure that they are thread-safe as Singleton beans are shared across multiple threads.
  3. Lazy Initialization: Consider using lazy initialization for your Singleton beans if they are resource-heavy during startup. This is achieved by adding @Lazy annotation to your bean definition.

Remember, the key with Singleton scope is understanding that you have one and only one instance per Spring IoC container. Therefore, every interaction with a Singleton bean will interact with the same state and hence should be designed accordingly.

Bean Prototype Scope

In the Spring Framework, a prototype scope signifies that a new instance of a bean will be created and returned every time a request for that bean is made. In simpler terms, when you set a bean to the prototype scope, Spring does not manage the complete lifecycle of that bean; the container instantiates, configures, and otherwise assembles a prototype object, and hands it to the client, with no further record of that instance.

Use cases for Prototype Scope

Now, you might be wondering where such a scope might come into play. The prototype scope is perfect for stateful beans where each instance can hold data that is independent from the others. So if you have a bean that is intended to be used and operated independently by multiple objects or in multiple operations within your application, the prototype scope can be a good fit.

An Example of a Bean with Prototype Scope

Let’s say we have a Message bean that is intended to carry a message with a timestamp. Every time we use this bean, we would want a new instance to ensure that the timestamp corresponds to the time of the bean creation. Here’s how you would define this in your Spring configuration:

@Configuration
public class AppConfig {

    @Bean
    @Scope("prototype")
    public Message message() {
        return new Message();
    }
}

public class Message {
    private LocalDateTime timestamp;
    private String content;

    public Message() {
        this.timestamp = LocalDateTime.now();
    }

    // getters and setters
}

In this configuration, the message() bean is defined with a “prototype” scope. A new instance, with its unique timestamp, will be created each time the Message bean is requested. This ensures that each instance of Message can have independent state and behavior.

You can test the above Message class with the following test method.

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;

import java.time.LocalDateTime;

import static org.junit.jupiter.api.Assertions.*;

@SpringBootTest
public class MessageTest {

    @Autowired
    private ApplicationContext applicationContext;

    @Test
    public void givenPrototypeScope_whenSetContent_thenDifferentContent() {
        Message messagePrototypeA = (Message) applicationContext.getBean("message");
        Message messagePrototypeB = (Message) applicationContext.getBean("message");

        messagePrototypeA.setContent("Hello");
        messagePrototypeB.setContent("World");

        assertEquals("Hello", messagePrototypeA.getContent());
        assertEquals("World", messagePrototypeB.getContent());
    }

    @Test
    public void givenPrototypeScope_whenGetTimestamp_thenDifferentTimestamp() {
        Message messagePrototypeA = (Message) applicationContext.getBean("message");
        Message messagePrototypeB = (Message) applicationContext.getBean("message");

        LocalDateTime timestampA = messagePrototypeA.getTimestamp();
        LocalDateTime timestampB = messagePrototypeB.getTimestamp();

        assertNotEquals(timestampA, timestampB);
    }
}

The first test method, givenPrototypeScope_whenSetContent_thenDifferentContent, checks that two message beans created from the same prototype scope have different content when they are set with different values. This verifies that the prototype scope creates a new instance of the bean every time it is requested.

The second test method, givenPrototypeScope_whenGetTimestamp_thenDifferentTimestamp, checks that two message beans created from the same prototype scope have different timestamps when they are initialized. This verifies that the prototype scope does not reuse the same instance of the bean for different requests.

Best Practices for Prototype Scope

While using the prototype scope can be very beneficial, it’s important to remember a few best practices.

  • Firstly, remember that Spring does not manage the complete lifecycle of a prototype bean: destruction lifecycle callbacks are not called. The client code must clean up prototype-scoped objects and release expensive resources that the prototype bean(s) are holding.
  • Secondly, use the prototype scope sparingly. Creating a new instance of a bean every time it’s needed can be quite expensive in terms of memory and processing time, particularly for heavyweight stateful beans.
  • Finally, if you inject a prototype-scoped bean into a singleton bean, the prototype bean still behaves like a singleton bean. This is because the singleton bean is only created once, and thus only gets injected with a single instance of the prototype bean. To get around this, you can use Spring’s method injection feature.

Request Scope

A Request Scope in Spring is one of the web-aware scopes provided by the framework. As the name suggests, it scopes a single bean definition to the lifecycle of a single HTTP request. In other words, each HTTP request has its own instance of a bean created off the back of a single bean definition. So, every time you make a new HTTP request, a new bean instance is created and is unique to that request.

Use Cases for Request Scope

There are several scenarios where the Request Scope is quite useful. One typical use case is when you’re dealing with form data submission and binding. Suppose you’re developing a web application where users can fill out a form. Each user’s form data is unique to that specific HTTP request. In such a case, the form bean should be request-scoped to keep the data specific to the individual requests.

Another use case is when you need to maintain state across several components during a single HTTP request, but you don’t want to store that state in a shared, global object like an HTTP session.

An Example of a Bean with Request Scope

To make this concept more concrete, let’s take a look at how you’d define a bean with Request Scope in your Spring configuration:

@Bean
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public MyClass myClass() {
    return new MyClass();
}

In this example, the MyClass class is defined as a Spring bean with Request Scope. The @Scope annotation is used to specify the scope of the bean. WebApplicationContext.SCOPE_REQUEST sets the scope to request, meaning a new instance of this bean will be created for each HTTP request.

The proxyMode attribute of the @Scope annotation is set to ScopedProxyMode.TARGET_CLASS, which means that Spring will create a class-based proxy for the bean. In simple terms, a proxy is a ‘stand-in’ object created by Spring, which ensures that the correct instance of the bean (one per request in this case) is injected where it’s needed, even if the injection target is a singleton bean.

Alternatively, you can use the @RequestScope annotation instead of @Scope(value = WebApplicationContext.SCOPE_REQUEST). It’s a specialized form of the @Scope annotation specifically for the request scope:

import org.springframework.web.context.annotation.RequestScope;
import org.springframework.stereotype.Component;

@Component
@RequestScope
public class MyRequestScopedBean {

    private String message;

    public MyRequestScopedBean() {
        message = "Initial Message";
    }

    public String getMessage() {
        return this.message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

Since MyRequestScopedBean is annotated with @RequestScope, a new instance of this bean will be created for each HTTP request, and the message field will be unique to each request.

Best Practices for Request Scope

When working with Request Scoped beans in the Spring Framework, here are some best practices to follow:

  • Understand that Request Scoped beans are stateful and not shared across multiple requests. These beans should not be used for sharing information between different HTTP requests or users.
  • Keep the beans in this scope lightweight. As the lifecycle of these beans is tied to an HTTP request, any heavy-duty operations in the constructor could potentially slow down the response time and lead to a poor user experience.
  • Remember that other beans that are not Request Scoped, like Singleton beans, will not be recreated for each request. If you inject a Request Scoped bean into a Singleton bean, the Request Scoped bean will not behave as expected unless you use a proxy.
  • Always ensure to clean up any resources that the Request Scoped bean might be holding before the request ends. This includes resources like database connections, file streams, etc., to prevent any resource leaks.

Session Scope

In the Spring Framework, when a bean is defined to be of session scope, it means that a single instance of the bean is created and managed for each HTTP session. In simpler terms, the bean is stored in the HTTP session, and the same instance is returned each time it is required within the same session. This is quite different from the singleton scope, where one instance is shared across the entire application, or the prototype scope, where a new instance is created every time the bean is required.

When to use Session Scope?

Session scope is particularly useful when you need to preserve the state of a bean throughout a user’s interaction with your application within a single session. A good example could be a shopping cart in an online store. You’d want the cart to be unique to each user session, storing their selected items as they navigate through the site. Session scope is perfect for such scenarios, as it ensures the bean’s state is separate and persistent for each user session.

Example of a Bean with a Session Scope

Here’s a simple example of how you can define a session-scoped bean using the @Scope annotation:

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Component
@Scope("session")
public class ShoppingCart {
    // Shopping cart implementation
}

Additionally, the @Scope annotation allows for two parameters, value and proxyMode, which is useful when you want to inject a session scoped bean into a singleton bean:

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.context.annotation.ScopedProxyMode;

@Component
@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class ShoppingCart {
    // Shopping cart implementation
}

In recent versions of Spring, you can also use the @SessionScope annotation, which is more specific:

import org.springframework.web.context.annotation.SessionScope;
import org.springframework.stereotype.Component;

@Component
@SessionScope
public class ShoppingCart {
    // Shopping cart implementation
}

In all these cases, the ShoppingCart bean will be specific to an HTTP session. Remember, proxyMode helps to create a proxy to be injected into your beans, allowing the Spring container to ensure that the bean is used in the correct context even when injected into a singleton bean.

Best Practices for Session Scope

When working with session-scoped beans, there are several best practices to keep in mind:

  1. Lightweight Session Beans: Try to keep your session-scoped beans lightweight. Remember that a new instance is created for each user session, so heavy beans could lead to memory issues.
  2. Use When Necessary: Session scope is handy but comes with added complexity and memory usage. Use it only when you need to maintain state across multiple requests within a single session.
  3. Thread Safety: While each session will have its own instance of the bean, keep in mind that HTTP sessions are not thread-safe. If multiple threads are involved within a session, additional synchronization might be necessary.
  4. Session Termination: Be aware of what happens when a session ends. The bean’s lifecycle is tied to the session, so once the session ends, so does the lifecycle of the bean. Plan for this in your bean’s design.

Application Scope

In Spring Framework, the Application Scope is one of the several available bean scopes. When a bean is defined with Application Scope, it means that the same instance of the bean is shared across the entire ServletContext. This means that once the bean is initialized, it lives for the entire lifetime of the application and is shared among all the requests and sessions. This is different from other scopes where beans can have lifetimes as short as a single HTTP request or as long as a single HTTP session.

When to use Application Scope?

Application Scope beans are beneficial when you need to maintain consistency across your application, and you need all users and components to interact with the same instance of a bean. This might be useful, for instance, when you have application-wide settings or when you want to cache data that is expensive to retrieve and can be used throughout the application.

Example of a Bean with an Application Scope

Here’s a basic example showing how to define a bean with Application Scope using the @Scope annotation:

@Component 
@ApplicationScope 
public class ApplicationScopeBean {
  // class body here 
}

Alternatively, you can use an @ApplicationScope annotation instead of @Scope(WebApplicationContext.SCOPE_APPLICATION).

@ApplicationScope is a specialization of @Scope for a component whose lifecycle is bound to the current web application. It also sets the default proxyMode to TARGET_CLASS, which means that a CGLIB proxy will be created for the scoped bean.

Best Practices for Application Scope

When working with Application Scope, it’s essential to be aware of a few best practices to avoid potential issues.

  1. Thread Safety: Since Application Scope beans are shared across all requests and sessions, multiple threads might access them concurrently. Therefore, they need to be thread-safe to prevent data inconsistency.
  2. Statelessness: It’s generally a good idea to keep Application Scope beans stateless or immutable, as they exist for the entire lifetime of the application and are shared among all components.
  3. Memory Consumption: Given that these beans exist for the whole life of the application, they could consume significant memory if not handled carefully. Thus, ensure these beans do not hold onto heavy resources for longer than necessary.
  4. Initialization Time: As Application Scope beans are created on startup, they could increase the initialization time of your application. Consider this while planning the startup time of your application.

WebSocket Scope

First off, let’s tackle what a WebSocket Scope is. As we’ve learned, scopes in Spring determine the lifecycle and visibility of a Spring Bean. The WebSocket Scope, as the name suggests, is tied to the lifecycle of a WebSocket. A bean defined in this scope lives within the duration of a WebSocket and is disposed of when the WebSocket is closed.

When to use WebSocket Scope?

So, when should you use a WebSocket Scope? The answer lies in the nature of your application. If your application relies on WebSockets for bi-directional, real-time communication between the server and clients, and you need a bean that is specific to each WebSocket session, then this scope is the right fit. This allows you to have stateful beans, with data that is specific to a WebSocket session and isolated from other sessions.

Example of a Bean with a WebSocket Scope

Now, let’s move to a practical example. Here’s a simple declaration of a WebSocket-scoped bean:

@Configuration
public class AppConfig {

    @Bean
    @Scope(scopeName = "websocket", proxyMode = ScopedProxyMode.TARGET_CLASS)
    public MyBean myBean() {
        return new MyBean();
    }
}

In this code snippet, @Scope is used with a scope name of ‘websocket’, indicating that this bean is specific to a WebSocket session. The proxyMode attribute is set to ScopedProxyMode.TARGET_CLASS to create a CGLIB proxy for the scoped bean. Remember, the WebSocket scope must be configured explicitly for it to be available in your application.

Best Practices for WebSocket Scope

Finally, let’s talk about some best practices when dealing with WebSocket Scope.

  1. Use Appropriately: Use WebSocket scoped beans only when you need data to be specific to a WebSocket session. If the data is not session-specific, consider using a different scope.
  2. Manage Resources: As WebSocket scoped beans live for the duration of a WebSocket, ensure you manage resources effectively to prevent memory leaks.
  3. Understand Proxies: Make sure to understand the implications of using proxies when working with scoped beans. Incorrect usage of proxy modes can lead to unexpected behavior.

Summary

In this tutorial, you’ve taken a deep dive into the world of Spring Bean Scopes. Here are the key points you’ve learned:

  1. Bean Scopes: Spring Bean Scopes are fundamental in controlling the lifecycle and visibility of Spring Beans within an application.
  2. Types of Scopes: You’ve learned about various types of scopes available in the Spring Framework, including Singleton, Prototype, Request, Session, Application, and WebSocket scopes.
  3. When to Use Which Scope: Each scope serves a different purpose and is used under different circumstances. Singleton beans are used when a single instance should be shared across multiple components. Prototype beans are used when you need a new instance every time a bean is requested. Request, Session, and Application scopes are used in web applications for creating beans with a lifecycle tied to HTTP requests, sessions, or the entire web application. WebSocket scope is used when you want to tie a bean’s lifecycle to a WebSocket session.
  4. Scope Annotation: Spring provides the @Scope annotation for specifying the scope of a bean at the time of its declaration.
  5. Proxies and Scopes: You’ve learned about the proxy modes (TARGET_CLASS, NO, DEFAULT) and their importance in managing scoped beans correctly.
  6. Best Practices: You’ve understood some of the best practices to follow when working with various Spring Bean Scopes, such as using the appropriate scope based on the application’s needs and managing resources effectively to prevent memory leaks.

Remember, understanding the correct use of Spring Bean Scopes can significantly impact the efficiency and effectiveness of your Spring applications. Keep exploring and practicing to make the best out of this powerful feature offered by the Spring Framework.