📘 Premium Read: Access my best content on Medium member-only articles — deep dives into Java, Spring Boot, Microservices, backend architecture, interview preparation, career advice, and industry-standard best practices.
🎓 Top 15 Udemy Courses (80-90% Discount): My Udemy Courses - Ramesh Fadatare — All my Udemy courses are real-time and project oriented courses.
▶️ Subscribe to My YouTube Channel (176K+ subscribers): Java Guides on YouTube
▶️ For AI, ChatGPT, Web, Tech, and Generative AI, subscribe to another channel: Ramesh Fadatare on YouTube
As Java developers, we often hear about design patterns. Some are used frequently, some are misunderstood, and some — like the Singleton Pattern — are baked right into the frameworks we use daily.
In fact, if you’re working with Spring Boot, you’ve probably been using singletons without even realizing it.
This article breaks down:
- What the Singleton Pattern really is
- Why it matters in Spring Boot
- How Spring manages singleton beans under the hood
- A real-world e-commerce example using singleton services
- Best practices and when to be careful
Let’s get into it.
What Is the Singleton Pattern?
In simple terms, the Singleton Pattern ensures that:
Only one instance of a class exists across the entire application.
This is useful when:
- You want to avoid duplicate objects
- You need a centralized point of control (e.g., configuration, caching, logging)
- You’re managing shared state or resources
Plain Java Singleton Example:
public class AppConfig {
private static AppConfig instance;
private AppConfig() {}
public static AppConfig getInstance() {
if (instance == null) {
instance = new AppConfig();
}
return instance;
}
}
This is a classic singleton. But in Spring Boot, you don’t usually write it like this. Spring does the job for you.
How Spring Boot Uses Singleton by Default
In Spring, beans are singleton by default.
That means:
- You define a class with
@Component
or@Service
- Spring creates only one instance of that class
- It’s managed in the application context and shared across all dependent components
Example:
@Service
public class EmailService {
public void send(String to, String subject, String body) {
// logic to send email
}
}
Now if OrderService
, UserService
, and AdminService
all inject EmailService
, they get the same instance.
@Autowired
private EmailService emailService;
🎯 That’s a Singleton Pattern in action — no extra code required.
Real-World Example: E-Commerce Order Processing
Let’s say we’re building an e-commerce app where we process customer orders. We want to:
- Validate the cart
- Check inventory
- Process payment
- Send confirmation email
We’ll use Spring Boot and apply the Singleton Pattern naturally through Spring-managed beans.
✅ Step 1: Create a Singleton Email Service
@Service
public class EmailService {
public void sendOrderConfirmation(String customerEmail, String orderId) {
System.out.println("Sending confirmation email to " + customerEmail + " for Order #" + orderId);
// In real life: use JavaMailSender, templates, etc.
}
}
This bean is a singleton by default.
✅ Step 2: Create Inventory Service
@Service
public class InventoryService {
public boolean isInStock(String productId, int quantity) {
// Simulate DB call
return true; // Assume always in stock for now
}
}
Again, this is a singleton bean.
✅ Step 3: Order Service (uses Singleton Services)
@Service
public class OrderService {
private final EmailService emailService;
private final InventoryService inventoryService;
@Autowired
public OrderService(EmailService emailService, InventoryService inventoryService) {
this.emailService = emailService;
this.inventoryService = inventoryService;
}
public void placeOrder(String customerEmail, String productId, int quantity) {
if (!inventoryService.isInStock(productId, quantity)) {
throw new RuntimeException("Product is out of stock!");
}
String orderId = UUID.randomUUID().toString(); // Simulated order ID
System.out.println("Order placed: " + orderId);
emailService.sendOrderConfirmation(customerEmail, orderId);
}
}
Here, OrderService
depends on two singleton services: EmailService
and InventoryService
.
✅ Step 4: Controller to Test It
@RestController
@RequestMapping("/api/orders")
public class OrderController {
private final OrderService orderService;
@Autowired
public OrderController(OrderService orderService) {
this.orderService = orderService;
}
@PostMapping
public ResponseEntity<String> placeOrder(@RequestParam String email,
@RequestParam String productId,
@RequestParam int quantity) {
orderService.placeOrder(email, productId, quantity);
return ResponseEntity.ok("Order placed successfully!");
}
}
Run your Spring Boot app and hit the endpoint:
POST http://localhost:8080/api/orders?email=test@example.com&productId=123&quantity=1
✅ It works. And guess what?
- All services were singletons
- No manual singleton code required
- Managed cleanly by Spring
How Can You Confirm It's a Singleton?
Let’s test it.
Modify EmailService
and add:
@PostConstruct
public void init() {
System.out.println("EmailService initialized: " + this);
}
You’ll see the message only once in the logs — no matter how many times you inject or use it.
That proves Spring created only one instance.
🎯 Spring’s IoC container ensures singleton scope by default unless you specify otherwise.
Scope Options in Spring
If you ever want a different scope, Spring gives you options:
Scope | Description |
---|---|
singleton | One instance per Spring context (default) |
prototype | New instance every time it's requested |
request | One instance per HTTP request (web apps) |
session | One per HTTP session (web apps) |
For most stateless services, singleton is perfect.
When Singleton May NOT Be Ideal
Singletons are powerful — but be cautious:
1. Stateful Services
Don’t store per-user or per-request state inside a singleton bean. It’s shared across users.
❌ Bad:
@Service
public class CartService {
private List<String> items = new ArrayList<>(); // shared!
}
✅ Better:
Pass data as parameters, or store it in request/session scope if needed.
2. Thread Safety
Singleton beans are shared — meaning multiple threads may use them. If your bean modifies shared variables, use proper synchronization or avoid mutability.
3. Testing and Isolation
Singletons can create hidden dependencies between tests if not cleaned up. Prefer using mock beans and Spring’s test context management.
Summary
Topic | Key Takeaway |
---|---|
Singleton pattern | Ensures only one instance of a class exists |
Spring beans | Singleton by default |
Use case | Stateless services like email, payment, logging |
Real-world usage | Inject same instance into multiple components |
Caution | Avoid storing state or mutable data in singletons |
Final Thoughts
The Singleton Pattern is one of the most used — and most misunderstood — design patterns. But when you're working in a Spring Boot project, it's built right in.
You don’t have to write getInstance()
or manage private constructors. Just annotate your service class, and let Spring handle the rest.
Use singletons for shared, stateless logic. Keep them clean, testable, and thread-safe — and you’ll be using one of the oldest patterns in modern, production-grade applications.
Comments
Post a Comment
Leave Comment