Spring BootSpring Corebeginner
Updated:

Spring Bean Scopes: Singleton, Prototype and Web Scopes

7 min read

Singleton or prototype? Request or session? Spring's bean scopes decide how many instances exist and how long they live. Here is each scope, when to use it, and the traps.

TL;DR – Quick Answer

A Spring bean scope controls how many instances of a bean the container creates and how long each lives. The default is singleton: one shared instance per container, reused everywhere. Prototype creates a new instance every time the bean is requested. Web-aware scopes — request, session and application — tie a bean's lifetime to an HTTP request, a user session, or the whole servlet context. You set a scope with the @Scope annotation.

On This Page

Once you know that a Spring bean is a container-managed object, the next question is: how many of it exist, and how long does each live? That is what a bean scope decides. Pick the wrong scope and you either waste memory or, worse, share mutable state between users and create bugs that only appear under load.

This page walks through every scope Spring offers — singleton, prototype and the web scopes — with the reasoning for when each fits and the concurrency trap that catches almost everyone at least once.

Singleton: the default, one shared instance

Unless you say otherwise, every bean is a singleton: the container creates exactly one instance and hands that same object to everyone who depends on it.

import org.springframework.stereotype.Service;

@Service   // no @Scope means singleton
public class TaxCalculator {
    // one instance shared across the whole application
    public double taxOn(double amount) {
        return amount * 0.18;
    }
}

Every class that injects TaxCalculator receives the identical object. For a stateless service like this — it holds no per-user data, just logic — that is perfect and efficient. This is why the beans you define following Spring beans are singletons by default and why most of your services should stay that way.

The catch is right there in "shared": one instance, many threads. That leads directly to the most important warning on this page.

Common mistake: Putting mutable, request-specific state in a singleton bean's fields. Because every thread shares the one instance, two users' requests will stomp on each other's data. Keep singleton beans stateless — pass per-request data as method parameters, never store it in fields.

Prototype: a new instance every time

Set the scope to prototype and the container builds a brand-new instance on every request for that bean.

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

@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class ShoppingCart {
    private final List<String> items = new ArrayList<>();  // per-instance state

    public void add(String item) {
        items.add(item);
    }
}

Each time something asks the container for a ShoppingCart, it gets its own cart with its own items list. This is exactly right for stateful objects that must not be shared. The tradeoff is that Spring stops managing the object after creation — it runs init callbacks but never destroy callbacks for prototypes, so any cleanup is on you. That lifecycle difference is explained further in the bean lifecycle.

The singleton-holds-prototype trap

A subtle problem appears when you inject a prototype into a singleton. Because the singleton is built only once, its prototype dependency is injected only once too — so you get a single fixed instance, not a fresh one per call.

@Service   // singleton
public class OrderProcessor {
    // Injected ONCE when OrderProcessor is created — NOT a new cart per order!
    private final ObjectProvider<ShoppingCart> cartProvider;

    public OrderProcessor(ObjectProvider<ShoppingCart> cartProvider) {
        this.cartProvider = cartProvider;
    }

    public void process() {
        ShoppingCart freshCart = cartProvider.getObject();  // NOW you get a new one
        // use freshCart for this order only
    }
}

Injecting the ShoppingCart directly would freeze one instance for the singleton's whole life. Injecting an ObjectProvider (or a scoped proxy) and calling getObject() each time gives you the fresh prototype you actually wanted. This is a favourite interview question because it reveals whether you have really used prototype scope or only read about it — see the Spring Boot 2 years experience interview set.

Web scopes: request, session, application

In a web application the container becomes web-aware and three more scopes unlock, each tying a bean's life to an HTTP concept.

Scope Lives for Typical use
request One HTTP request Per-request context, request-scoped data
session One user session A logged-in user's preferences, cart
application The whole ServletContext App-wide shared config
import org.springframework.web.context.annotation.RequestScope;

@Component
@RequestScope   // one instance per HTTP request
public class RequestAuditContext {
    private String requestId;
    private long startTime;
    // safely holds data for THIS request only
}

A @RequestScope bean is created when a request begins and discarded when it ends, so holding per-request state in its fields is safe — unlike doing the same in a singleton. Spring provides @RequestScope, @SessionScope and @ApplicationScope as convenient shortcuts for the corresponding @Scope values. These only apply once you are building web endpoints, the kind you create in building a REST API with Spring Boot.

Why singleton is safe despite being shared

New learners often worry: if one singleton instance serves every request, surely two users' requests collide? The answer lies in where state lives. A singleton bean is shared, but the local variables inside its methods are not — each thread that calls a method gets its own stack frame with its own local variables and parameters.

@Service
public class InvoiceService {

    // NO mutable fields — everything the method needs comes in as parameters
    public Invoice generate(Order order, Customer customer) {
        double subtotal = order.total();      // local: unique per thread/call
        double tax = subtotal * 0.18;         // local: unique per thread/call
        return new Invoice(customer, subtotal, tax);
    }
}

Because InvoiceService holds no mutable fields and keeps all per-request data in local variables and parameters, a thousand threads can share the single instance safely. This is the golden rule of singleton beans: no mutable shared state in fields. Follow it and the default scope is both efficient and correct — which is exactly why Spring makes it the default and why most services in the Spring beans guide stay singleton.

Setting a scope explicitly

You have seen @Scope and the web-scope shortcuts, but it is worth collecting the ways to declare a scope in one place, because interviewers sometimes ask you to write it.

// Long form, using the string constant
@Component
@Scope("prototype")
public class ReportBuilder { }

// Long form for a web scope, with a proxy so it can be injected into a singleton
@Component
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class RequestContext { }

The proxyMode on the second example is the clean solution to the singleton-holds-shorter- scope problem from earlier: Spring injects a proxy that, on each call, resolves the correct request-scoped instance. It is an alternative to the ObjectProvider approach and the one you will most often see in web code. Both exist because a longer-lived bean cannot hold a direct reference to a shorter-lived one without help.

Choosing the right scope

The decision is almost always driven by state:

  • Stateless logic (calculators, validators, most services) — singleton. One instance, shared, efficient.
  • Per-user or per-operation state that must not leak — prototype, or a web scope if the state maps to a request or session.
  • Web request/session data — the matching web scope, so lifetime is handled for you.

Start from singleton and only widen when you have a concrete reason. Reaching for prototype "just in case" usually signals state that should not be in a bean at all.

Pro tip: In interviews, tie scope back to threading: "Singleton is the default and it is shared across threads, so it must be stateless. When I need per-user state, I switch to prototype or a web scope." Connecting scope to thread-safety is exactly the depth interviewers want, and it links to the wider inversion of control model that the container is built on.

Scope in the wider container picture

It helps to place scope alongside the other bean concepts so they form one model rather than separate facts. Defining a bean decides that it exists; its scope decides how many exist and how long each lives; its lifecycle decides what happens to each instance from birth to death. All three are managed by the same inversion of control container, and all three are things you declare while the container does the work.

Scope specifically answers a question the container must resolve every time a dependency is requested: do I hand back an existing instance, or create a new one? Singleton says "reuse", prototype says "create fresh", and the web scopes say "reuse within this request or session, create fresh for the next". Once you see scope as the container's caching-and-lifetime policy for a bean, the annotations stop being trivia and become a design decision you make deliberately.

Bringing it together

A bean scope answers "how many, and for how long". Singleton — the default — gives one shared instance and demands statelessness. Prototype gives a fresh instance per request but leaves cleanup to you. The web scopes — request, session, application — tie a bean's life to HTTP concepts and make per-request state safe. Set any of them with @Scope or the web-scope shortcuts.

Most beans should stay singleton; reach for a wider scope only when state forces it, and remember the singleton-holds-prototype trap. From here, continue through the Spring Boot learning path into dependency injection and the bean lifecycle to complete your grip on how the container manages objects.

Frequently Asked Questions

What is the default scope of a Spring bean?
Singleton. Spring creates exactly one instance of the bean per container and returns that same shared instance to every class that depends on it. This is efficient but means singleton beans must be stateless or thread-safe, because many threads share the one instance.
What is the difference between singleton and prototype scope?
A singleton bean is created once and shared everywhere, so all injection points receive the same object. A prototype bean is created fresh every time it is requested from the container, so each injection point gets its own instance. Use singleton for stateless services and prototype when each user needs independent state.
What are the web scopes in Spring?
Request, session and application. A request-scoped bean lives for one HTTP request, a session-scoped bean lives for a user's session across requests, and an application-scoped bean lives for the entire ServletContext. These only work in a web-aware application context.
Why is a prototype bean not destroyed by Spring?
Because Spring hands off a prototype instance to the caller and stops tracking it. The container calls initialization callbacks but not destruction callbacks for prototypes, so cleanup is the caller's responsibility. This differs from singletons, whose full lifecycle the container manages.
What happens if you inject a prototype bean into a singleton?
The prototype is injected only once, when the singleton is created, so you effectively get a single fixed instance rather than a new one each time. To get a fresh prototype per use, inject an ObjectProvider or use a proxy, rather than relying on plain field injection.

Want to Build Your Career in Java Full Stack with AI?

Join CodeBegun and train with working industry engineers — Explore the Program

Apply for Demo Class →
Siva Prasad Galaba
Founder, CodeBegun · Staff Engineer

Founder of CodeBegun. 15+ years building Java systems at companies like Crunchyroll. Teaches Java, Spring Boot and system design the way the industry actually works, and mentors students through projects, mock interviews and placement preparation.

Technically reviewed by CodeBegun Technical TeamLast reviewed 15 July 2026 LinkedIn
Chat with us