๐ŸŒฑ Spring Boot Bean Scopes

Understanding Bean Scopes in Spring Boot is essential for controlling how and when objects are created inside the Spring IoC container.


๐Ÿ“Œ 1. What is Bean Scope?

A Bean Scope defines:


๐Ÿ— 2. Default Scope: Singleton

โœ… Definition

๐Ÿ“ฆ Example

import org.springframework.stereotype.Component;

@Component
public class EmailService {

    public EmailService() {
        System.out.println("EmailService instance created");
    }
}

๐Ÿ” Behavior

@Autowired
private EmailService emailService;

โœ” Only one object is created and reused everywhere.

๐Ÿง  Internal Working Diagram

Spring Container
       |
       |----> [ EmailService ]  (Single Instance)
       |           โ†‘
       |           โ†‘
 Controller1   Controller2

๐ŸŸข When to Use Singleton?


๐Ÿ” 3. Prototype Scope

โœ… Definition

๐Ÿ“ฆ Example

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Component
@Scope("prototype")
public class PaymentService {

    public PaymentService() {
        System.out.println("PaymentService instance created");
    }
}

๐Ÿ” Behavior

@Autowired
private PaymentService paymentService;

Every injection or manual getBean() call โ†’ creates a new object

๐Ÿง  Internal Working Diagram

Request 1 โ†’ [ PaymentService Instance 1 ]
Request 2 โ†’ [ PaymentService Instance 2 ]
Request 3 โ†’ [ PaymentService Instance 3 ]

โš  Important Warning

When injecting prototype into singleton:

@Component
public class OrderService {

    @Autowired
    private PaymentService paymentService;
}

โ— Only one prototype instance is injected at startup.

To get new instance every time, use:

Example:

@Autowired
private ObjectProvider<PaymentService> provider;

public void process() {
    PaymentService service = provider.getObject();
}

๐ŸŒ 4. Web Scopes (Used in Web Applications)

These require Spring Web dependency.

๐Ÿ”น Request Scope

โœ… Definition

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.web.context.WebApplicationContext;

@Component
@Scope(WebApplicationContext.SCOPE_REQUEST)
public class RequestBean {
}

๐Ÿง  Diagram

HTTP Request 1 โ†’ [ RequestBean Instance 1 ]
HTTP Request 2 โ†’ [ RequestBean Instance 2 ]

๐Ÿ”น Session Scope

โœ… Definition

@Scope(WebApplicationContext.SCOPE_SESSION)
@Component
public class SessionBean {
}

๐Ÿง  Diagram

User A Session โ†’ [ SessionBean A ]
User B Session โ†’ [ SessionBean B ]

๐Ÿ”น Application Scope

โœ… Definition

@Scope(WebApplicationContext.SCOPE_APPLICATION)
@Component
public class ApplicationBean {
}

๐Ÿ“Š 5. Scope Comparison Table

Scope Instances Created Shared Across Typical Use Case
Singleton 1 per container Entire app Services, Repos
Prototype Every request Not shared Stateful objects
Request Per HTTP request Per request Request data
Session Per session Per user User session data
Application Per web app Entire app Global web data

โš™ 6. How Spring Manages Scopes Internally

           Spring IoC Container
                    |
        -----------------------------
        |            |             |
    Singleton    Prototype      Web Scopes
        |            |             |
   Created at    Created on     Created per
   startup       demand         HTTP lifecycle

๐Ÿงช 7. Real-World Scenario Example

๐Ÿ›’ E-Commerce Application

Component Recommended Scope
ProductService Singleton
Cart (per user) Session
PaymentTransaction Prototype
RequestLogger Request

๐ŸŽฏ 8. Best Practices


โ“ 9. Interview Questions

  1. What is the default scope in Spring Boot?
  2. Difference between Singleton and Prototype?
  3. What happens when Prototype is injected into Singleton?
  4. How do you create a new prototype bean every time?

๐Ÿš€ Summary

Bean scopes control:

Understanding scopes helps you:

If you would like this exported, say:

Download as PDF / DOCX / PPT / Markdown / TXT ๐Ÿ“„


๐Ÿงฉ 10. Advanced Scope Handling Techniques

๐Ÿ”น Using ObjectProvider

ObjectProvider allows you to lazily fetch a new bean instance when required.

import org.springframework.beans.factory.ObjectProvider;
import org.springframework.stereotype.Service;

@Service
public class OrderService {

    private final ObjectProvider<PaymentService> provider;

    public OrderService(ObjectProvider<PaymentService> provider) {
        this.provider = provider;
    }

    public void processOrder() {
        PaymentService paymentService = provider.getObject();
        paymentService.toString();
    }
}

โœ” Each call to getObject() returns a new Prototype instance.


๐Ÿ”น Using @Lookup

@Lookup lets Spring override a method to return a new Prototype bean each time.

import org.springframework.beans.factory.annotation.Lookup;
import org.springframework.stereotype.Component;

@Component
public abstract class ReportService {

    public void generate() {
        PrototypeBean bean = createPrototypeBean();
        bean.toString();
    }

    @Lookup
    protected abstract PrototypeBean createPrototypeBean();
}

โœ” Spring dynamically generates implementation at runtime.


๐Ÿ”น Using @Lazy

@Lazy delays bean initialization until it is actually needed.

import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

@Component
@Lazy
public class HeavyService {

    public HeavyService() {
        System.out.println("HeavyService initialized");
    }
}

โœ” Bean is NOT created at application startup.

โœ” It is created only when first requested.

@Autowired
@Lazy
private HeavyService heavyService;

โœ” Useful for performance optimization and breaking circular dependencies.


๐Ÿ†š 11. When to Prefer ObjectProvider Over @Lookup

Both ObjectProvider and @Lookup solve the same problem:

Getting a fresh Prototype bean inside a Singleton bean

๐Ÿ“Š Quick Comparison

Feature ObjectProvider @Lookup
Type Programmatic (explicit call) Declarative (method override)
Flexibility Very High Limited
Optional Beans Supported Not Supported
Stream Support Yes No
Testing Friendly Easier to mock Harder
Recommended in modern apps Yes Rarely

๐Ÿฅ‡ Prefer ObjectProvider When

1๏ธโƒฃ You Want Explicit Control

@Service
public class OrderService {

    private final ObjectProvider<PaymentService> provider;

    public OrderService(ObjectProvider<PaymentService> provider) {
        this.provider = provider;
    }

    public void process() {
        PaymentService service = provider.getObject();
        service.processPayment();
    }
}

โœ” Clear and explicit bean retrieval

โœ” Easier debugging


2๏ธโƒฃ You Need Optional Beans

PaymentService service = provider.getIfAvailable();

โœ” No exception if bean does not exist


3๏ธโƒฃ You Need Default Fallback

PaymentService service =
        provider.getIfAvailable(DefaultPaymentService::new);

4๏ธโƒฃ You Need Stream Support

provider.stream()
        .forEach(PaymentService::processPayment);

5๏ธโƒฃ You Care About Testability

when(provider.getObject()).thenReturn(mockPaymentService);

โœ” Easy to mock in unit tests


๐Ÿฅˆ When to Use @Lookup

@Component
public abstract class ReportService {

    public void generate() {
        PrototypeBean bean = createPrototypeBean();
        bean.generate();
    }

    @Lookup
    protected abstract PrototypeBean createPrototypeBean();
}

โš  Spring overrides method at runtime using CGLIB subclassing.


๐Ÿง  Internal Difference Diagram

ObjectProvider

Singleton Bean
      |
      | calls getObject()
      โ†“
Spring Container โ†’ NEW Prototype instance

@Lookup

Spring creates subclass
      |
Overrides method
      |
Each call โ†’ Container โ†’ NEW Prototype

๐ŸŽฏ Final Recommendation


๐Ÿงฉ 12. @Lookup Deep Dive โ€“ Legacy Behavior & Multiple Beans Handling

๐Ÿ“Œ What is @Lookup Internally?

@Lookup is method injection. Spring overrides the method at runtime using CGLIB and performs a BeanFactory lookup every time the method is called.

@Lookup
protected abstract PrototypeBean createPrototypeBean();

Internally behaves like:

@Override
protected PrototypeBean createPrototypeBean() {
    return applicationContext.getBean(PrototypeBean.class);
}

๐Ÿ•ฐ @Lookup in Legacy XML Configuration

Before annotations, lookup method injection was configured in XML:

<lookup-method name="createPrototypeBean" bean="prototypeBean"/>

Example:

<bean id="reportService" class="com.example.ReportService">
    <lookup-method name="createPrototypeBean" bean="prototypeBean"/>
</bean>
Modern Legacy
@Lookup <lookup-method>

๐Ÿ”€ Multiple Beans Scenario

If two prototype beans exist:

@Component
@Scope("prototype")
public class PaymentService { }
@Component
@Scope("prototype")
public class StripePaymentService extends PaymentService { }

Using:

@Lookup
protected abstract PaymentService createPaymentService();

Spring throws:

NoUniqueBeanDefinitionException:
Expected single matching bean but found 2

Fix by specifying bean name:

@Lookup("stripePaymentService")
protected abstract PaymentService createPaymentService();

๐Ÿ” How This Differs from ObjectProvider

@Autowired
private ObjectProvider<PaymentService> provider;

provider.getObject()

Throws exception if multiple beans exist.

provider.stream()

provider.stream()
        .forEach(PaymentService::processPayment);

โœ” Returns all matching beans
โœ” No exception
โœ” More flexible resolution


๐Ÿ“Š Behavior Comparison

Scenario @Lookup ObjectProvider
Single Bean Works Works
Multiple Beans Exception (unless name specified) Exception for getObject()
Stream All Beans No Yes
Optional Bean No Yes
Fallback Default No Yes

๐ŸŽฏ Final Recommendation