Spring Bean Scopes


In Spring Framework, bean scope defines the lifecycle and visibility of a bean — i.e., how many instances of a bean are created and how they are shared within the container. Spring provides several built-in scopes (some are for regular apps, others specific to web apps).


1. singleton (default)


Definition: Only one instance of the bean per Spring IoC container. 

  • Single instance is cached in the container.
  • All @Autowired injections of that bean will point to the same instance.
  • In Spring, every bean has singleton scope by default unless you explicitly specify another scope using @Scope (or XML config).

Best for: Reusable components.

Bean type: Stateless

Real-time use case:

  • Service layer classes (UserService, ProductService)
  • DAO classes (UserRepository, OrderRepository)
  • Utility classes like logging or configuration readers

Example:


@RestController

@RequestMapping("/users")

public class UserController {

    private final UserService userService; // singleton


    public UserController(UserService userService) {

        this.userService = userService;

    }


    @GetMapping("/{id}")

    public User getUser(@PathVariable Long id) {

        return userService.getUserById(id);

    }

}


@Service

public class UserService { // By default singleton scoped

    private final UserRepository userRepository;


    public UserService(UserRepository userRepository) {

        this.userRepository = userRepository;

    }


    public User getUserById(Long id) {

        return userRepository.findById(id).orElseThrow();

    }

}


@Repository

public interface UserRepository extends JpaRepository<User, Long> { }


Use case: Shared, stateless beans (business logic, database access, controllers)
Analogy: CEO of a company – only one CEO manages the entire organization.
👉 Almost 90% of beans in real Spring Boot apps are singletons.



2. prototype


Definition: A new bean instance is created each time it’s requested.
Best for: Stateful beans where each caller needs a fresh instance.

Bean type: Stateful

Real-time use case:

  • PDF reports (each report generation is independent)
  • Temporary user session data (non-web contexts)
  • Game objects (different players/characters)

Example:


@Component

@Scope("prototype")

public class ReportGenerator {

    public void generateReport() {

        System.out.println("New Report instance: " + this);

    }

}


@Service

public class ReportService {

    private final ObjectProvider<ReportGenerator> reportProvider;


    public ReportService(ObjectProvider<ReportGenerator> reportProvider) {

        this.reportProvider = reportProvider;

    }


    public void createReport() {

        ReportGenerator generator = reportProvider.getObject(); // new instance

        generator.generateReport();

    }

}


Use case: PDF/Excel generation, notification builders, temporary objects
Analogy: Visitor badge at office – every time a visitor comes, they get a new badge.



Stateless vs Stateful Beans


  • Stateless Beans: Do not hold any user-specific or instance-specific data between method calls. Safe to use as a singleton because the same instance can serve multiple users without conflicts.
    Example: Service layer classes like UserService or utility classes like logging.
  • Stateful Beans: Hold data specific to a user or a task. If shared (singleton), the state can leak between requests/users. Prototype scope ensures each caller gets a fresh instance.
    Example: PDF report generation, shopping carts, session-based data.




Singleton vs Prototype: Why Stateful Beans Need Prototype?


Sometimes, beans store user-specific data (stateful). If we use singleton for such beans, data can leak between users. Prototype scope solves this.



1. Singleton Example (Problematic)


@Component // By default, Spring beans are singleton-scoped

public class SingletonPdfGenerator {

    private String fileName;


    public SingletonPdfGenerator() {

        this.fileName = "Invoice_" + UUID.randomUUID() + ".pdf";

    }


    public void generate(String customerName) {

        System.out.println("Using " + fileName + " for " + customerName);

    }

}


@Service

public class SingletonInvoiceService {

    private final SingletonPdfGenerator pdfGenerator;


    public SingletonInvoiceService(SingletonPdfGenerator pdfGenerator) {

        this.pdfGenerator = pdfGenerator;

    }


    public void createInvoice(String customerName) {

        pdfGenerator.generate(customerName);

    }

}


Output:

Using Invoice_1234.pdf for Alice

Using Invoice_1234.pdf for Bob

Using Invoice_1234.pdf for Charlie


👉 Problem:

  • Same file reused for all users → risk of data corruption
  • Singleton is unsafe for stateful tasks

Analogy: Using the same visitor badge for everyone — confusion is inevitable.




2. Prototype Example (Correct Approach)


@Component

@Scope("prototype")

public class PrototypePdfGenerator {

    private String fileName;


    public PrototypePdfGenerator() {

        this.fileName = "Invoice_" + UUID.randomUUID() + ".pdf";

    }


    public void generate(String customerName) {

        System.out.println("Using " + fileName + " for " + customerName);

    }

}


@Service

public class PrototypeInvoiceService {

    private final ObjectProvider<PrototypePdfGenerator> pdfProvider;


    public PrototypeInvoiceService(ObjectProvider<PrototypePdfGenerator> pdfProvider) {

        this.pdfProvider = pdfProvider;

    }


    public void createInvoice(String customerName) {

        PrototypePdfGenerator pdf = pdfProvider.getObject(); // NEW instance every call

        pdf.generate(customerName);

    }

}


Output:

Using Invoice_9876.pdf for Alice

Using Invoice_6543.pdf for Bob

Using Invoice_3210.pdf for Charlie


Correct Behavior:

  • Each user gets a new instance → no overlap
  • Prototype scope ensures stateful beans remain independent



Key Takeaways


Scope

Behavior for Stateful Tasks

Singleton

One object reused → unsafe for tasks storing user-specific data

Prototype

New object per request → safe and isolated


Analogy:

  • Singleton: One shared visitor badge → causes mix-ups
  • Prototype: Each visitor gets their own badge → no conflict



3. request (web-specific)


Definition: New bean instance per HTTP request
Best for: Request-specific data processing

Bean type: Stateful

Real-time use case: Request metadata (requestId, headers), form validation


Example:


@Component

@Scope("request")

public class RequestMetadata {

    private final String requestId = UUID.randomUUID().toString();

    public String getRequestId() { return requestId; }

}


@RestController

@RequestMapping("/orders")

public class OrderController {

    private final RequestMetadata requestMetadata;


    public OrderController(RequestMetadata requestMetadata) {

        this.requestMetadata = requestMetadata;

    }


    @GetMapping

    public String placeOrder() {

        return "Order placed with Request ID: " + requestMetadata.getRequestId();

    }

}


Use case: Request tracing, audit logging, validation
Analogy: Order receipt in a restaurant – every order generates a new receipt.



4. session (web-specific)


Definition: One bean instance per HTTP session
Best for: User-specific data across multiple requests

Bean type: Stateful

Real-time use case: Shopping cart, user preferences, authentication tokens


Example:


@Component

@Scope("session")

public class ShoppingCart {

    private final List<String> items = new ArrayList<>();

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

    public List<String> getItems() { return items; }

}


@RestController

@RequestMapping("/cart")

public class CartController {

    private final ShoppingCart shoppingCart;


    public CartController(ShoppingCart shoppingCart) {

        this.shoppingCart = shoppingCart;

    }


    @PostMapping("/add/{item}")

    public String addItem(@PathVariable String item) {

        shoppingCart.addItem(item);

        return "Item added: " + item;

    }


    @GetMapping

    public List<String> viewCart() {

        return shoppingCart.getItems();

    }

}


Use case: Session-based authentication, user preferences
Analogy: Shopping cart – same cart for the entire session.



5. application (web-specific)


Definition: Single bean per ServletContext (web app)
Best for: Application-wide shared resources

Bean type: Stateless

Real-time use case: Cache, global configuration, application statistics


Example:


@Component

@Scope("application")

public class AppStatistics {

    private int visitorCount = 0;

    public synchronized void incrementVisitorCount() { visitorCount++; }

    public int getVisitorCount() { return visitorCount; }

}


@RestController

@RequestMapping("/stats")

public class StatisticsController {

    private final AppStatistics statistics;


    public StatisticsController(AppStatistics statistics) {

        this.statistics = statistics;

    }


    @GetMapping

    public String getStats() {

        statistics.incrementVisitorCount();

        return "Total visitors: " + statistics.getVisitorCount();

    }

}


Use case: Global counters, cache managers
Analogy: Notice board – visible to everyone.



6. websocket (web-specific)


Definition: One bean instance per WebSocket session
Best for: Real-time communication

Bean type: Stateful

Real-time use case: Chat apps, live streams, multiplayer games


Example:


@Component

@Scope("websocket")

public class ChatSession {

    private String username;

    public void setUsername(String username) { this.username = username; }

    public String getUsername() { return username; }

}


Use case: Chat apps, live streams
Analogy: Private call line – each user has their own connection.



Defining Bean Scope


1. Annotation-based:

@Component

@Scope("prototype")

public class MyBean { }


2. XML-based:

<bean id="myBean" class="com.example.MyBean" scope="prototype"/>



Summary Table of Scopes 


Scope

Description

Lifecycle

Real-Time Use Cases

singleton

One bean per container (default)

Container-wide

Services, DAOs, Utilities

prototype

New bean per request

Caller-managed

Report generators, Temporary objects

request

One bean per HTTP request

Request duration

Request tracking, Form validation

session

One bean per HTTP session

Session duration

Shopping cart, User preferences

application

One bean per web app

Application duration

Caches, App stats, Global config

websocket

One bean per WebSocket session

WebSocket duration

Chat session, Live streams, Games



Spring bean scopes in terms of stateless vs stateful:


Scope

Stateless or Stateful

Explanation

singleton    

Stateless

Safe to share because it does not hold user-specific or temporary data. Example: Service or DAO beans.

prototype    

Stateful

Each caller gets a new instance, so it can hold user-specific or task-specific data safely. Example: PDF report generator.

request

Stateful

Bean lives for one HTTP request → can hold request-specific data safely. Example: Request metadata.

session

Stateful

Bean lives for one HTTP session → holds user session data. Example: Shopping cart.

application    

Stateless

Bean is shared across the whole app → used for global resources. Example: Cache manager, global statistics.

websocket        

Stateful

Bean lives for a WebSocket session → holds connection-specific data. Example: Chat session.