Spring Framework Tutorial 0/14 lessons ~6 min read Lesson 6

    Bean Scopes & Lifecycle

    Every Spring bean has a scope that decides how many instances exist and how long they live.

    Course progress0%
    Focus
    8 guided sections
    Practice signal
    Examples included
    Career prep
    Foundation builder

    Introduction

    Every Spring bean has a scope that decides how many instances exist and how long they live. Pick the wrong scope and you get either subtle concurrency bugs (state leaking between users) or wasted memory (rebuilding expensive objects on every call).

    Most beans should be singletons. The interesting decisions happen when you need request- or session-bound state in a web app.

    Understanding the topic

    The scopes you will actually use:

    • singleton (default) — one instance per container. Use for stateless services.
    • prototype — a new instance every time you ask. Use for stateful helpers and builders.
    • request / session — web-only; one per HTTP request or session.
    • application — one per ServletContext (rarely needed).
    • websocket — one per WebSocket session.

    Syntax reference

    Declaring a non-default scope:

    java
    @Component
    @Scope("prototype")
    public class ReportBuilder { /* fresh state per use */ }
    // Injecting a request-scoped bean into a singleton needs a scoped proxy:
    @Component
    @Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
    public class CurrentUser {
    private String username;
    /* ... */
    }

    Informative example

    Why the proxy is necessary — and what happens without it:

    java
    @Service
    class AuditService {
    private final CurrentUser user; // request-scoped
    AuditService(CurrentUser user) { this.user = user; }
    public void log(String action) {
    // Without proxyMode = TARGET_CLASS, 'user' is the *first* request's
    // user — frozen forever inside this singleton. With the proxy, every
    // method call resolves to the user of the current request.
    System.out.println(user.getUsername() + " did " + action);
    }
    }

    Real-world use

    Memory leaks in long-running Spring apps almost always trace back to a misused scope: either a singleton accidentally caches per-user state, or a prototype is held alive by a singleton reference. Auditing scopes during code review is cheap and catches whole classes of bugs.

    Best practices

    • Default to singleton; only widen the scope when state truly belongs to a request or user.
    • If a singleton depends on a request-scoped bean, use @Scope(proxyMode = TARGET_CLASS).
    • Document why a bean is prototype — readers will assume singleton.

    Common mistakes

    • Storing mutable user data in a singleton service — race condition waiting to happen.
    • Forgetting that prototype beans don't get their @PreDestroy called by the container.

    Hands-on exercise

    Try this: create a counter bean with singleton scope and another with prototype. Inject both into a controller and hit an endpoint several times. Observe how only the prototype's count resets each request. Then switch the prototype to request scope (with a proxy) and observe the difference again.

    Ready to mark this lesson complete?Track your journey across the entire course.