Hello! Welcome to the next lesson in our journey through asynchronous and event-driven patterns.
In our last session, we configured a custom ThreadPoolTaskExecutor to gain fine-grained control over our asynchronous operations with @Async. This was a crucial step for making our application robust and production-ready. We successfully decoupled the execution of a task from the main thread.
However, the components themselves remained tightly coupled—one service had to explicitly call a method on another. Today, we'll explore a powerful pattern that decouples the components themselves: Spring's application events. You will learn how to define and publish custom application events using ApplicationEventPublisher. This will enable different parts of your application to communicate and react to state changes without being directly aware of one another, a cornerstone of modular and scalable software design.
1. From Direct Calls to Decoupled Events
Imagine a UserService that handles new user registrations. After a user is registered, the system needs to perform several follow-up actions:
- Send a welcome email.
- Assign a default user role.
- Log an audit trail.
If the UserService directly calls an EmailService, RoleService, and AuditService, it becomes tightly coupled to them. Any change in those services might require modifying the UserService. What if we later need to add another action, like adding the user to a marketing list? We'd have to change the UserService again. This violates the Open/Closed Principle of SOLID design.
Spring Events offer a solution based on the Observer design pattern.
- The
UserService(the publisher) simply announces, "A user has registered!" by publishing an event. - Other services (the listeners) that care about this event can subscribe to it and react accordingly.
The UserService doesn't know or care who is listening. This decoupling makes the system more modular, extensible, and easier to maintain.
To see this concept explained with a real-world example, let's watch a short segment from a video by Java Techie.
Spring Boot Application Events Explained with Real-Time Examples | @Javatechie
This video segment explains the problem of tight coupling using a 'patient discharge' scenario and introduces Spring Events as the solution for creating a loosely coupled design.
Watch from 00:57 to 07:46. Focus on understanding the four problems identified with the direct service-call approach: tight coupling, difficulty extending, complex testing, and synchronous processing. Then, see how publishing an event provides a cleaner alternative.
2. The Three Components of Spring Events
The Spring event mechanism consists of three main parts, as illustrated below. Today, we'll focus on the first two: the Event and the Publisher.

3. Step 1: Defining a Custom Event
An event is simply a message that signals something has happened. In Spring, this message is an object. This object's primary role is to carry relevant data about what occurred.
For modern Spring applications (version 4.2 and later), an event can be any Plain Old Java Object (POJO).
The article 'Spring Boot Events Tutorial' provides a clear and modern example of creating a custom event as a simple POJO. Let's look at their UserRegisteredEvent example.
Please read the section '4. How to Create and Publish an Event', focusing on subsection 'a) Create an Event (Custom POJO)'. Notice how the UserRegisteredEvent is a simple, immutable class designed to hold the new user's email.
Here is what a UserRegisteredEvent might look like. It's a simple class that holds the necessary data—in this case, the user's email.
// A simple POJO to represent our event
public class UserRegisteredEvent {
private final String email;
public UserRegisteredEvent(String email) {
this.email = email;
}
public String getEmail() {
return email;
}
}
A Note on Older Spring Versions:
In older codebases or tutorials (using Spring versions before 4.2), you will often see event classes extendingApplicationEvent. While this still works, it's no longer required. The modern approach of using simple POJOs is cleaner and more flexible. The Baeldung article "Spring Events" (Resource ID: LINK, section 2.1) shows this older style if you are curious.
4. Step 2: Publishing an Event
Once we have our event class, we need a way to publish it. Spring provides the ApplicationEventPublisher interface for this purpose. It's a bean that's automatically available in the application context, so you can inject it into any other bean (like a service) that needs to publish events.
The process is straightforward:
- Inject
ApplicationEventPublisherinto your service. Constructor injection is the recommended approach. - In the method where the event occurs (e.g.,
registerUser), create an instance of your custom event object. - Call the
publishEvent()method on the publisher, passing your event object.
Let's see this in practice.
The Baeldung article 'Spring Events' demonstrates how to create a publisher and use the ApplicationEventPublisher.
Read section '2.2. A Publisher'. It shows a class, CustomSpringEventPublisher, that injects ApplicationEventPublisher and uses it to publish an event. The principle is the same for our UserService example.
Applying this to our UserService example from earlier, the implementation would look like this:
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
@Service
public class UserService {
private final ApplicationEventPublisher eventPublisher;
// 1. Inject the publisher via the constructor
public UserService(ApplicationEventPublisher eventPublisher) {
this.eventPublisher = eventPublisher;
}
public void registerUser(String email, String password) {
// ... logic to save the user to the database ...
System.out.println("User registered: " + email);
// 2. Create the event object
UserRegisteredEvent event = new UserRegisteredEvent(email);
// 3. Publish the event
System.out.println("Publishing UserRegisteredEvent...");
eventPublisher.publishEvent(event);
System.out.println("User registration method finished.");
}
}
By default, the publishEvent() call is synchronous. This means the registerUser method will block and wait until all listeners have finished processing the event before it continues. We'll learn how to make listeners asynchronous in a future lesson.
To see a live-coding demonstration of event publishing, let's return to the Java Techie video.
Spring Boot Application Events Explained with Real-Time Examples | @Javatechie
This segment shows how to inject ApplicationEventPublisher and use its publishEvent method within the patient discharge example.
Watch from 13:29 to 15:17. Observe how the ApplicationEventPublisher is injected into the DischargeService and then used to publish the PatientDischargeEvent.
Test your understanding!
Imagine you are building an e-commerce application. You have an OrderService that finalizes an order. After an order is successfully created, you need to trigger two side effects: updating the inventory and sending an order confirmation email.
How would you refactor the OrderService to use Spring Events?
- What would you name your event class, and what key pieces of information would it contain?
- Show the code for how you would modify the
OrderService'splaceOrdermethod to publish this event.
Show answer
-
Event Class:
A good name would beOrderPlacedEvent. It should contain information that listeners would need, such as the order ID and customer ID.public class OrderPlacedEvent { private final Long orderId; private final Long customerId; public OrderPlacedEvent(Long orderId, Long customerId) { this.orderId = orderId; this.customerId = customerId; } public Long getOrderId() { return orderId; } public Long getCustomerId() { return customerId; } } -
Modified
OrderService:
You would injectApplicationEventPublisherand callpublishEvent()after the order is saved.import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; @Service public class OrderService { private final ApplicationEventPublisher eventPublisher; public OrderService(ApplicationEventPublisher eventPublisher) { this.eventPublisher = eventPublisher; } public void placeOrder(OrderData orderData) { // ... logic to validate and save the order ... // Assume the saved order has an ID of 'savedOrderId' // and belongs to 'customerId' System.out.println("Order " + savedOrderId + " saved successfully."); // Publish an event to notify other parts of the system OrderPlacedEvent event = new OrderPlacedEvent(savedOrderId, customerId); eventPublisher.publishEvent(event); } }
Conclusion
Great work today! You've learned a fundamental pattern for building decoupled, modular Spring applications. By using events, you can create components that react to changes in the system without being tightly bound to the source of the change.
Key Takeaways:
- Spring Events help decouple application components, following the Observer design pattern.
- An event is an object (typically a simple POJO) that carries data about something that happened.
- You can publish an event from any Spring bean by injecting the
ApplicationEventPublisherand calling itspublishEvent()method. - By default, event publication is a synchronous, blocking operation.
What's Next?
We've successfully published an event, but right now, nothing is happening in response because we haven't created any listeners. In our next lesson, we will complete the picture by learning how to create listeners to consume application events using the @EventListener annotation. You'll see just how easy it is to subscribe to the events we've just learned to publish.