Quick Look On Spring Bean Scopes

İbrahim Gündüz
4 min readJan 14, 2025

--

The bean scopes define the lifecycle and visibility of a bean in the context. The latest version of the spring framework documentation defines 6 types of scopes as below:

  • Singleton (default)
  • prototype
  • request
  • session
  • application
  • websocket

Singleton:

When a bean is defined with a singleton scope, the container creates a single instance of the bean and all requests for the bean receive the same instance. In other words, if the object state changes, this will be reflected in all the places reference to the bean. Singleton is the default scope unless any other scope is specified.

Let’s create the following class to exemplify the scope.

package org.example;

public class EmailTransporter {
private String sender;

public void setSender(String sender) {
this.sender = sender;
}

public String getSender() {
return sender;
}

public void sendEmail(String email, String message) {
System.out.println("Sending email to " + email + " with message: " + message + " from " + sender);
}
}

Afterwards, define a bean with Singleton scope. Defining the bean using the Scope(“singleton”) or like the following one would define the bean with singleton scope.

@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
public EmailTransporter emailTransporter() {
return new EmailTransporter();
}

As you can see from the following test, the second bean also would be affected by the first bean’s state change.

private final static String ROOT_PACKAGE = "org.example";
private final static String SENDER = "Sender 1";

@Test
void singleton_scope() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.scan(ROOT_PACKAGE);
context.refresh();

EmailTransporter bean1 = context.getBean(EmailTransporter.class);
EmailTransporter bean2 = context.getBean(EmailTransporter.class);

bean1.setSender(SENDER);

assertEquals(bean1.getSender(), bean2.getSender());
}

Prototype:

When a bean is defined with Prototype scope, the container returns a new instance of the bean for each request.

Let’s modify the bean definition by changing the scope type as below.

@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public EmailTransporter emailTransporter() {
return new EmailTransporter();
}

This time, the change that happened on the first instance would not affect the second one as you can see from the following test.

private final static String ROOT_PACKAGE = "org.example";
private final static String SENDER = "Sender 1";
private final static String SENDER2 = "Sender 2";

@Test
void prototype_scope() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.scan(ROOT_PACKAGE);
context.refresh();

EmailTransporter bean1 = context.getBean(EmailTransporter.class);
EmailTransporter bean2 = context.getBean(EmailTransporter.class);

bean1.setSender(SENDER);
bean2.setSender(SENDER2);

assertEquals(bean1.getSender(), SENDER);
assertEquals(bean2.getSender(), SENDER2);
}

The last four scope types are specific for web-aware applications.

Request:

This scope would allow Spring to create a new instance of the bean for each HTTP Request. Any state change on the bean during the request would not affect the other instances. In the following configuration change, you see that we set proxyMode additionally. Because there is no active request at the moment of web app context instantiation, Spring creates a proxy object to be injected as a dependency and instantiates the target object when it’s needed for a request.

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

And.. you can inject the bean by using the @Resource annotation as below:

@Controller
class OrderController {
@Resource(name = "flashMessage") FlashMessage flashMessage;

@PostMapping
public String complete() {
String message = flashMessage.getMessage();
//...
//...
return ...
}
}

Session

This scope allows Spring to create the bean instance for each HTTP Session started. The way of bean definition and instantiation of the bean works similarly to the request scope.

@Bean
@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
public ClientSettings clientPreferences() {
return new ClientSettings();
}

Or you can define the bean just by using @SessionScope annotation.

@Bean
@SessionScope
public ClientSettings clientPreferences() {
return new ClientSettings();
}

Application

When a bean is defined with Application scope, Spring shares the same bean instance across all the servlets running in the same ServletContext. If one of the components changes the bean state, the other recipients of the bean running in the same ServletContext would be affected regardless of the HTTP session or request.

@Bean
@Scope(value = WebApplicationContext.SCOPE_APPLICATION, proxyMode = ScopedProxyMode.TARGET_CLASS)
public ApplicationSettings applicationSettings() {
return new ApplicationSettings();
}

Or you can define the bean just by using @ApplicationScope annotation.

@Bean
@ApplicationScope
public ApplicationSettings applicationSettings() {
return new ApplicationSettings();
}

WebSocket

This scope allows Spring to create the bean for every WebSocket session. The same instance would be provided whenever accessed during the entire session.

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

Conclusion

In this article, I tried to explain the usage of the scope mechanism briefly. I think the last four types of scopes require more explanations and detailed examples. Also maybe it would have been good to touch on some potential issues in regards to testing caused by the limitations of Resource annotation. But let’s keep it simple for the moment, and meet in another article with more details.

Thanks for reading!

Credits

--

--

No responses yet