Spring Boot Bean scopes

Default scope: singleton (one instance per Spring ApplicationContext). Spring reuses the same bean for every injection/call.

Note: This is not the GoF Singleton patternโ€”each ApplicationContext has its own instance.

Core scopes (available everywhere)

Scope What it means Typical use
singleton (default) One instance per ApplicationContext Services, controllers, repositories (stateless)
prototype New instance every injection / getBean() Stateful helpers, per-use objects

Web scopes (need a web-aware context: Spring MVC/WebFlux)

Scope What it means Typical use
request One instance per HTTP request Request-scoped DTO, per-request context
session One instance per HTTP session Shopping cart, user preferences
application One per ServletContext Cross-servlet shared state (rare)
websocket One per WebSocket session Per-socket state in STOMP/websocket apps

How to set a scope

Annotation-based components

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

@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) // or "prototype"
public class MyPrototype {}

@Bean methods

import org.springframework.context.annotation.*;
import org.springframework.web.context.WebApplicationContext;

@Configuration
public class AppConfig {

  @Bean
  @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
  MyPrototype proto() { return new MyPrototype(); }

  @Bean
  @Scope(value = WebApplicationContext.SCOPE_REQUEST,
         proxyMode = ScopedProxyMode.TARGET_CLASS) // important when injecting into singletons
  RequestScopedBean reqBean() { return new RequestScopedBean(); }
}

Convenience annotations (web)

import org.springframework.web.context.annotation.RequestScope;
import org.springframework.web.context.annotation.SessionScope;

@RequestScope
@Component
class PerRequestCtx {}

@SessionScope
@Component
class Cart {}

Injecting non-singletons into singletons (the โ€œgotchaโ€)

A singleton bean is created at startup; if it receives a prototype/request bean directly, that dependency is resolved once. Use one of:

  1. Scoped proxy

@Component
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
class RequestUserContext {}

@Component
class ServiceA {
  private final RequestUserContext ctx; // injected as a proxy โ†’ resolves per request
  ServiceA(RequestUserContext ctx) { this.ctx = ctx; }
}
  1. Provider/ObjectFactory (create on demand)

import org.springframework.beans.factory.ObjectProvider;

@Component
class ServiceB {
  private final ObjectProvider<MyPrototype> provider;
  ServiceB(ObjectProvider<MyPrototype> provider) { this.provider = provider; }

  void doWork() {
    MyPrototype p = provider.getObject(); // new instance each call
  }
}

Best practices

  • Keep most beans stateless singletons (thread-safe).
  • Use prototype sparingly; note that Spring does not manage its full lifecycle after creation (no destroy callbacks).
  • For web scopes, prefer @RequestScope/@SessionScope and scoped proxies when injecting into singletons.
  • Avoid storing large objects in session/request beans.

(Advanced) Custom/thread scope

Spring has a SimpleThreadScope you can register:

import org.springframework.context.support.SimpleThreadScope;
import org.springframework.beans.factory.config.CustomScopeConfigurer;

@Bean
public static CustomScopeConfigurer customScopes() {
  CustomScopeConfigurer c = new CustomScopeConfigurer();
  c.addScope("thread", new SimpleThreadScope());
  return c;
}
// Then: @Scope("thread")

If you tell me your exact use case (e.g., โ€œneed per-request user context in a serviceโ€), Iโ€™ll show the minimal wiring with proxies or providers.

Back to blog

Leave a comment