# Spring Beans Demystified — Scopes, Lifecycle, and Lazy Injection in a Credit Card Eligibility Microservice

💡 Introduction

Working on a credit card eligibility microservice recently taught me a lot about Spring’s bean system. I had to make design choices that ensured thread-safety, clean state management, and efficient memory use — especially when dealing with stateful evaluations per user.

In this post, I’ll walk through:

* What Spring beans are
    
* Their lifecycle
    
* Different scopes
    
* Lazy injection and why it’s a lifesaver
    
* Real examples from a credit card eligibility context
    

---

### ☕ What is a Spring Bean?

A **bean** in Spring is simply an object that is managed by the Spring container. When you annotate a class with `@Component`, `@Service`, or define it using `@Bean`, Spring takes care of:

* Instantiating it
    
* Injecting its dependencies
    
* Managing its lifecycle
    

Example:

```java
@Component
public class CreditScoreService {
    public int getScore(String userId) {
        // Dummy logic
        return 720;
    }
}
```

Spring creates and manages a `CreditScoreService` bean.

---

### 🔄 Bean Lifecycle (Simplified)

1. **Instantiation** – Spring creates the object.
    
2. **Dependency Injection** – Injects all required fields/constructors.
    
3. **PostConstruct** – Runs any method annotated with `@PostConstruct`.
    
4. **Bean is ready to use.**
    
5. **PreDestroy** – If it's a singleton and being destroyed, `@PreDestroy` methods are called.
    

```java
@Component
public class EligibilityLogger {

    @PostConstruct
    public void init() {
        System.out.println("Logger initialized");
    }

    @PreDestroy
    public void destroy() {
        System.out.println("Cleaning up logger");
    }
}
```

---

### 📦 Bean Scopes — Singleton vs Prototype

#### 🔹 Singleton (default)

* Only **one instance** is created per Spring context.
    
* Shared across the entire application.
    

Example:

```java
@Service
public class EligibilityService {
    // Singleton by default
}
```

✅ Good for stateless services, utilities, or shared components.

---

#### 🔹 Prototype

* A **new instance** is created **every time the bean is requested**.
    
* Ideal when the bean holds **request-specific data or state**.
    

---

### 🧪 Example from Credit Card Eligibility

Let’s say we have a service that evaluates whether a user is eligible for a credit card based on credit score and income.

#### ✅ Step 1: Define a stateful bean

```java
@Component
@Scope("prototype")
public class EligibilityEvaluationContext {
    private boolean eligible;
    private String rejectionReason;
    private Map<String, Object> debugInfo = new HashMap<>();

    // Getters & Setters
}
```

> Why prototype? Because this context is **specific to each request** and shouldn't be shared.

---

#### ✅ Step 2: Inject it using `ObjectFactory` (Lazy Injection)

```java
@Service
public class EligibilityService {

    @Autowired
    private CreditScoreService scoreService;

    @Autowired
    private ObjectFactory<EligibilityEvaluationContext> contextFactory;

    public EligibilityEvaluationContext evaluate(String userId) {
        int score = scoreService.getScore(userId);
        EligibilityEvaluationContext context = contextFactory.getObject();

        if (score < 650) {
            context.setEligible(false);
            context.setRejectionReason("Low credit score");
            context.getDebugInfo().put("score", score);
        } else {
            context.setEligible(true);
        }

        return context;
    }
}
```

👉 `ObjectFactory.getObject()` ensures we get a **fresh** `EligibilityEvaluationContext` every time — this avoids bugs due to shared state and keeps our service thread-safe.

---

### ⚠️ Why Not Inject Prototype Directly?

```java
@Autowired
private EligibilityEvaluationContext context;
```

This would only create one instance — during startup — and reuse it everywhere. Totally defeats the point of `@Scope("prototype")`.

---

### 🧊 Bonus: Lazy Initialization with `@Lazy`

If a bean is **expensive to create** and might **not be used right away**, you can delay its creation:

```java
@Autowired
@Lazy
private ExternalScoringClient scoringClient;
```

Now, `scoringClient` will only be created **when it's first accessed** — not during app startup.

---

### 🧠 Key Takeaways

* **Use Singleton** for stateless services and utilities.
    
* **Use Prototype** for stateful, request-specific objects.
    
* Use `ObjectFactory` or `Provider` to inject prototype beans into singleton services safely.
    
* Use `@Lazy` to defer expensive bean creation until needed.
    

---

### 📌 Conclusion

Understanding how Spring manages beans is crucial for designing clean, safe microservices — especially in user-facing financial applications like credit card approvals.

If you're ever storing per-user evaluation data or debugging context, don’t rely on singletons. Prototype + lazy injection is your friend.
