PrepZone Logo
PrepZone

Bean Scopes and Lifecycle

Singleton, prototype, request and session scopes — and the callbacks between birth and destruction.

Why this matters

  • Injecting request-specific state into a singleton bean is a common bug that causes data leaks between BookStore customers.
  • Understanding lifecycle callbacks lets you open resources at startup and close them on shutdown cleanly.
  • Scope questions appear in interviews because the wrong scope silently corrupts concurrent requests.

Singleton scope (default)

Java
@Service  // scope defaults to singleton
public class BookService { ... }

One instance per ApplicationContext. Every HTTP request shares the same BookService. This is correct for stateless services that delegate to repositories.

Never store request data in a singleton field:

Java
@Service
public class BookService {
    private String currentUser;  // BUG: shared across all requests!
}

Prototype scope

Java
@Component
@Scope("prototype")
public class SearchCriteriaBuilder {
    private List<String> filters = new ArrayList<>();
    // each injection gets a fresh instance
}

A new instance every time the bean is requested. Useful for builder objects or per-operation state. Spring does not manage prototype destruction — you own cleanup.

Web scopes

Scopes for web applications

  • request — One instance per HTTP request; ideal for request-scoped DTOs.
  • session — One instance per HTTP session; shopping cart state in BookStore.
  • application — One instance per ServletContext; rarely used directly.

Enable web scopes with @EnableWebMvc or by using spring-boot-starter-web (already active in BookStore).

Java
@Component
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class RequestContext {
    private String correlationId;
}

proxyMode is required when injecting a shorter-lived bean into a singleton — Spring creates a proxy that resolves the real instance per request.

SingletonOne instance per container
PrototypeNew instance every injection
RequestOne per HTTP request
SessionOne per HTTP session
Singleton is the default. Choose other scopes when lifecycle must match HTTP session or request.

Lifecycle callbacks

Beans can hook into creation and destruction:

Java
@Service
public class InventorySyncService {

    @PostConstruct
    public void warmCache() {
        // runs after dependency injection completes
        log.info("Inventory cache warmed");
    }

    @PreDestroy
    public void flushPending() {
        // runs before context shutdown
        log.info("Flushing pending inventory updates");
    }
}

Alternative: implement InitializingBean and DisposableBean interfaces — functionally identical but less idiomatic.

Startup ordering with @DependsOn

When BookService must start after ReferenceDataLoader:

Java
@Service
@DependsOn("referenceDataLoader")
public class BookService { ... }

@Component
public class ReferenceDataLoader {
    @PostConstruct
    public void load() {
        // populate genre lookup table
    }
}

@DependsOn controls creation order without constructor coupling.

Lazy initialisation

Java
@Service
@Lazy
public class ReportGenerator {
    public ReportGenerator(ExpensiveDataSource ds) { ... }
}

Bean creation deferred until first use. Breaks circular dependencies but delays failure detection. Use sparingly in BookStore — fail fast at startup is usually better.

Scope in practice for BookStore

ComponentRecommended scopeReason
BookServicesingletonStateless business logic
BookRepositorysingletonBacked by connection pool
ShoppingCartsessionPer-user cart state
RequestIdHolderrequestPer-request correlation ID
  • BookService

    Recommended scopesingleton
    ReasonStateless business logic
  • BookRepository

    Recommended scopesingleton
    ReasonBacked by connection pool
  • ShoppingCart

    Recommended scopesession
    ReasonPer-user cart state
  • RequestIdHolder

    Recommended scoperequest
    ReasonPer-request correlation ID

Observing bean creation

Enable debug logging to see instantiation order:

Java
logging:
  level:
    org.springframework.beans.factory: DEBUG

Each Creating instance of bean log line shows the container's creation sequence.

Quick recall

Everything you need if you only revisit this box.

  • Default scope is singleton — one shared instance for the entire application.
  • Never store per-request state in singleton fields.
  • Request and session scopes tie bean lifetime to HTTP lifecycle.
  • Use proxyMode = TARGET_CLASS when injecting web-scoped beans into singletons.
  • @PostConstruct runs after injection; @PreDestroy runs before shutdown.
  • @DependsOn controls bean creation order without constructor dependencies.

Test yourself

Answer these before moving on — recall is what makes it stick.