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:
@Component
public class CreditScoreService {
public int getScore(String userId) {
// Dummy logic
return 720;
}
}
Spring creates and manages a CreditScoreService
bean.
🔄 Bean Lifecycle (Simplified)
Instantiation – Spring creates the object.
Dependency Injection – Injects all required fields/constructors.
PostConstruct – Runs any method annotated with
@PostConstruct
.Bean is ready to use.
PreDestroy – If it's a singleton and being destroyed,
@PreDestroy
methods are called.
@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:
@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
@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)
@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?
@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:
@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
orProvider
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.
Subscribe to my newsletter
Read articles from Shravani Thirunagari directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Shravani Thirunagari
Shravani Thirunagari
Hi there, I am a product developer primarily skilled in Java(SpringBoot), Node JS, and ES6 (React JS). I have around 7 years of experience in delivering end-to-end products. My expertise lies in implementing optimized solutions using relevant tech stacks. As part of this, I like to explore and learn new technologies. I am a curious student.