Observer Pattern in Java with Examples

📘 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

👋 Introduction

In Java applications, it’s common to deal with events or changes that other parts of the system need to react to. For example:

  • When a user places an order, you might need to send an email, update inventory, and log the event.
  • When a user updates their profile, other components might need to sync that change elsewhere.

Doing all of that in one method can lead to tightly coupled code and poor separation of concerns.

This is where the Observer Pattern comes in.

It allows you to build a publish/subscribe system where observers automatically get notified when something changes — without the subject needing to know who they are or what they do.

In this article, we’ll explore:

  • What the Observer Pattern is
  • A real-world Java example
  • A Spring Boot use case
  • When and why to use it
  • When not to use it

Let’s get started.

What is the Observer Pattern?

The Observer Pattern defines a one-to-many relationship between objects, so that when one object (the "subject") changes state, all its dependents (observers) are notified and updated automatically.

You have two key parts:

  • Subject: The main object that holds the data or triggers the event
  • Observer(s): Components that want to listen for changes or updates

Think of it like YouTube: When you subscribe to a channel, you get notified whenever they upload a new video.


❌ The Problem Without Observer Pattern

Let’s say you have a system where users place orders, and you need to:

  1. Save the order to the database
  2. Send a confirmation email
  3. Notify the shipping team
  4. Log the activity

Without the Observer Pattern, you might write everything in the same method:

public void placeOrder(Order order) {
    saveToDatabase(order);
    sendConfirmationEmail(order);
    notifyShipping(order);
    logOrder(order);
}

❌ This mixes responsibilities ❌ Hard to maintain ❌ Adding a new behavior requires editing this method


✅ Observer Pattern Fix

With the Observer Pattern, you separate each responsibility into its own class and let them "subscribe" to order events. When an order is placed, each observer is notified automatically.


Real-World Java Example: Order Event System

Let’s walk through a basic implementation.


Step 1: Define the Observer Interface

public interface OrderObserver {
    void onOrderPlaced(Order order);
}

Step 2: Create the Subject (Publisher)

public class OrderService {
    private final List<OrderObserver> observers = new ArrayList<>();

    public void addObserver(OrderObserver observer) {
        observers.add(observer);
    }

    public void placeOrder(Order order) {
        System.out.println("Placing order: " + order.getId());

        // Notify all observers
        for (OrderObserver observer : observers) {
            observer.onOrderPlaced(order);
        }
    }
}

Step 3: Create Observer Implementations

public class EmailNotifier implements OrderObserver {
    public void onOrderPlaced(Order order) {
        System.out.println("Sending email for order: " + order.getId());
    }
}

public class InventoryUpdater implements OrderObserver {
    public void onOrderPlaced(Order order) {
        System.out.println("Updating inventory for order: " + order.getId());
    }
}

public class OrderLogger implements OrderObserver {
    public void onOrderPlaced(Order order) {
        System.out.println("Logging order: " + order.getId());
    }
}

Step 4: Putting It All Together

public class Main {
    public static void main(String[] args) {
        Order order = new Order("ORD-123");

        OrderService orderService = new OrderService();
        orderService.addObserver(new EmailNotifier());
        orderService.addObserver(new InventoryUpdater());
        orderService.addObserver(new OrderLogger());

        orderService.placeOrder(order);
    }
}

Order Class (for completeness)

public class Order {
    private final String id;

    public Order(String id) {
        this.id = id;
    }

    public String getId() {
        return id;
    }
}

Output:

Placing order: ORD-123  
Sending email for order: ORD-123  
Updating inventory for order: ORD-123  
Logging order: ORD-123

✅ Each observer is separate ✅ Easy to add/remove observers ✅ OrderService doesn’t care who listens


Spring Boot Use Case: Application Events

Spring Boot provides built-in support for the Observer Pattern using ApplicationEventPublisher and @EventListener.

Let’s implement a similar order event system using Spring Boot.


Step 1: Define the OrderPlacedEvent

public class OrderPlacedEvent extends ApplicationEvent {
    private final String orderId;

    public OrderPlacedEvent(Object source, String orderId) {
        super(source);
        this.orderId = orderId;
    }

    public String getOrderId() {
        return orderId;
    }
}

Step 2: Publish the Event

@Service
public class OrderService {

    private final ApplicationEventPublisher publisher;

    public OrderService(ApplicationEventPublisher publisher) {
        this.publisher = publisher;
    }

    public void placeOrder(String orderId) {
        System.out.println("Placing order: " + orderId);

        // Publish event
        publisher.publishEvent(new OrderPlacedEvent(this, orderId));
    }
}

Step 3: Create Observers Using @EventListener

@Component
public class EmailListener {

    @EventListener
    public void handle(OrderPlacedEvent event) {
        System.out.println("Email: Order " + event.getOrderId() + " received.");
    }
}

@Component
public class InventoryListener {

    @EventListener
    public void handle(OrderPlacedEvent event) {
        System.out.println("Inventory: Reserved items for order " + event.getOrderId());
    }
}

@Component
public class LoggerListener {

    @EventListener
    public void handle(OrderPlacedEvent event) {
        System.out.println("Log: Order event for " + event.getOrderId());
    }
}

Step 4: Controller to Trigger Order

@RestController
@RequestMapping("/orders")
public class OrderController {

    private final OrderService service;

    public OrderController(OrderService service) {
        this.service = service;
    }

    @PostMapping("/{id}")
    public ResponseEntity<String> placeOrder(@PathVariable String id) {
        service.placeOrder(id);
        return ResponseEntity.ok("Order " + id + " placed.");
    }
}

Request:

POST /orders/ORD-456

Output:

Placing order: ORD-456  
Email: Order ORD-456 received.  
Inventory: Reserved items for order ORD-456  
Log: Order event for ORD-456

✅ No tight coupling between components ✅ Easily add/remove listeners ✅ Follows Spring best practices


✅ Benefits of the Observer Pattern

Problem Without Observer Observer Pattern Fix
All logic in one method Each action in its own class
Hard to test or extend Observers are independent and testable
High coupling between classes Loose coupling through publish-subscribe
Changes require method rewrite Just add or remove observers

✅ When to Use the Observer Pattern

Use it when:

  • You have multiple components reacting to the same event
  • You want loose coupling between the event source and handlers
  • You need dynamic subscription behavior
  • You are working in an event-driven system (e.g., order events, login events)

🚫 When Not to Use It

Avoid it when:

  • You only have one or two consumers — simple method calls might be enough
  • You need strict execution order — observer order isn’t guaranteed
  • You want tight control over the logic path
  • You are handling sensitive workflows where auditing or rollback is important

🧪 Testability

Observer Pattern improves testability:

  • You can test each observer in isolation
  • No need to mock unrelated logic
  • You can verify whether an event is triggered using mock frameworks

Example:

@Test
void testEmailNotification() {
    OrderPlacedEvent event = new OrderPlacedEvent(this, "ORD-789");
    EmailListener listener = new EmailListener();

    listener.handle(event);

    // assert output or verify behavior
}

🏁 Conclusion

The Observer Pattern is a practical and powerful tool in Java. It helps you build clean, extensible systems by:

  • Decoupling logic
  • Improving maintainability
  • Supporting real-time reactions
  • Scaling without rewriting code

Whether you're building your own observer system or using Spring’s event model, the Observer Pattern makes your codebase more modular and easier to evolve.

That’s why Java developers rely on it — especially in systems that are growing, changing, or event-driven.

Comments

Spring Boot 3 Paid Course Published for Free
on my Java Guides YouTube Channel

Subscribe to my YouTube Channel (165K+ subscribers):
Java Guides Channel

Top 10 My Udemy Courses with Huge Discount:
Udemy Courses - Ramesh Fadatare