Spring Boot — Application Events
Use Spring's event system for decoupled communication: custom events, async listeners, and ordering.
import org.springframework.context.*;
import org.springframework.context.event.*;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.*;
import java.time.Instant;
// Custom event
public record OrderCreatedEvent(String orderId, String userId, double total, Instant timestamp) {
public OrderCreatedEvent(String orderId, String userId, double total) {
this(orderId, userId, total, Instant.now());
}
}
// Publishing events
@Service
class OrderService {
private final ApplicationEventPublisher publisher;
OrderService(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
public void createOrder(String orderId, String userId, double total) {
// ... save order to DB
System.out.println("Order created: " + orderId);
// Publish event (decoupled from listeners)
publisher.publishEvent(new OrderCreatedEvent(orderId, userId, total));
}
}
// Listeners — executed in publisher's thread by default
@Component
class EmailNotificationListener {
@EventListener
@Order(1) // execute first
public void onOrderCreated(OrderCreatedEvent event) {
System.out.printf("[Email] Sending confirmation for order %s to user %s%n",
event.orderId(), event.userId());
}
}
@Component
class InventoryListener {
@EventListener
@Order(2)
public void onOrderCreated(OrderCreatedEvent event) {
System.out.printf("[Inventory] Reserving stock for order %s%n", event.orderId());
}
}
// Async listener (non-blocking)
@Component
class AnalyticsListener {
@Async
@EventListener
public void onOrderCreated(OrderCreatedEvent event) {
System.out.printf("[Analytics] Recording order %s ($%.2f) on %s%n",
event.orderId(), event.total(), Thread.currentThread().getName());
}
}
// Conditional listener
@Component
class HighValueOrderListener {
@EventListener(condition = "#event.total() > 100")
public void onHighValueOrder(OrderCreatedEvent event) {
System.out.printf("[VIP] High value order %s: $%.2f%n",
event.orderId(), event.total());
}
}
// Transaction-bound listener
// @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
// public void afterCommit(OrderCreatedEvent event) {
// // Only runs after transaction successfully commits
// }Sponsored
Railway
Use Cases
- Decoupled event-driven service communication
- Async notification and side-effect processing
- Order processing pipelines with event listeners
Tags
Related Snippets
Similar patterns you can reuse in the same workflow.
CompletableFuture — Async Programming
Chain async operations with CompletableFuture: thenApply, thenCompose, allOf, and exception handling.
Best for: Parallel API calls to multiple services
Spring Boot REST Controller with CRUD
Create a complete REST API with Spring Boot: GET, POST, PUT, DELETE with validation and error handling.
Best for: Building RESTful APIs with Spring Boot
Spring Boot Global Exception Handler
Centralized error handling with @ControllerAdvice for validation errors, 404s, and custom exceptions.
Best for: Consistent error responses across all endpoints
Observer Pattern — Event System
Build a type-safe event system using the Observer pattern with generics and functional interfaces.
Best for: Decoupled event-driven architectures