Spring Boot Application Events
Events decouple your code — the publisher doesn’t know who’s listening. Add listeners without modifying existing code.
Custom Event
// Event class
public record BookCreatedEvent(Long bookId, String title, String author, Instant timestamp) {
public BookCreatedEvent(Long bookId, String title, String author) {
this(bookId, title, author, Instant.now());
}
}
Publishing Events
@Service
public class BookService {
private final BookRepository bookRepository;
private final ApplicationEventPublisher eventPublisher;
public BookService(BookRepository bookRepository, ApplicationEventPublisher eventPublisher) {
this.bookRepository = bookRepository;
this.eventPublisher = eventPublisher;
}
@Transactional
public BookResponse create(CreateBookRequest request) {
Book book = bookRepository.save(new Book(request.title(), request.author()));
// Publish event — anyone can listen
eventPublisher.publishEvent(new BookCreatedEvent(
book.getId(), book.getTitle(), book.getAuthor()));
return BookResponse.from(book);
}
}
Listening to Events
@Component
public class BookEventListeners {
@EventListener
public void onBookCreated(BookCreatedEvent event) {
log.info("Book created: {} by {}", event.title(), event.author());
}
}
@Component
public class NotificationListener {
private final EmailService emailService;
@EventListener
public void onBookCreated(BookCreatedEvent event) {
emailService.sendNewBookNotification(event.title(), event.author());
}
}
@Component
public class SearchIndexListener {
@EventListener
public void onBookCreated(BookCreatedEvent event) {
searchService.indexBook(event.bookId());
}
}
Three listeners, zero coupling —
BookServicedoesn’t know any of them exist.
Async Events
@Component
public class NotificationListener {
@Async
@EventListener
public void onBookCreated(BookCreatedEvent event) {
// Runs in a separate thread
emailService.sendNewBookNotification(event.title(), event.author());
}
}
@TransactionalEventListener
Execute after the transaction commits — ensures data is actually saved:
@Component
public class SearchIndexListener {
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void onBookCreated(BookCreatedEvent event) {
// Only runs if the transaction committed successfully
searchService.indexBook(event.bookId());
}
}
| Phase | When |
|---|---|
AFTER_COMMIT |
Transaction committed (default) |
AFTER_ROLLBACK |
Transaction rolled back |
AFTER_COMPLETION |
After commit or rollback |
BEFORE_COMMIT |
Before commit |
Built-in Spring Events
@EventListener
public void onApplicationReady(ApplicationReadyEvent event) {
log.info("Application is ready!");
}
@EventListener
public void onContextClosed(ContextClosedEvent event) {
log.info("Application shutting down");
}
Key Takeaways
- Events decouple publishers from listeners — add features without modifying existing code.
ApplicationEventPublisher.publishEvent()to publish.@EventListenerto listen — multiple listeners per event.@Async @EventListenerfor non-blocking event handling.@TransactionalEventListener(AFTER_COMMIT)— only runs after successful commit.
What’s Next?
Next: WebSocket — real-time bidirectional communication.