Spring Events — Java Interview Guide | Cracked Java
Senior

Spring Events

ApplicationEvent and @EventListener, sync vs async events, transactional event listeners and their phases, when to use events vs a message broker, and Spring Modulith.

Prereqs: aop-proxies

Spring's event mechanism is the in-JVM observer pattern wired into the container: one bean publishes an event, any number of beans listen, and neither knows about the other. It exists to decouple components that live in the same ApplicationContext — instead of OrderService calling EmailService and InventoryService directly, it publishes an OrderPlacedEvent and walks away. The listeners subscribe. Adding a new reaction means adding a listener, not editing the publisher.

You publish through ApplicationEventPublisher (inject it, or inject the ApplicationContext which implements it):

@Service
class OrderService {
    private final ApplicationEventPublisher events;
    OrderService(ApplicationEventPublisher events) { this.events = events; }

    void place(Order o) {
        // ... persist the order ...
        events.publishEvent(new OrderPlacedEvent(o.id()));
    }
}
@Component
class EmailListener {
    @EventListener
    void on(OrderPlacedEvent e) { /* send confirmation */ }
}

Since Spring 4.2 the event can be any object — no need to extend ApplicationEvent — and listeners are plain methods annotated with @EventListener, matched by parameter type. That's the modern API.

The single fact candidates most often get wrong: publishing is synchronous by default. publishEvent does not return until every listener has run, on the same thread, inside the same transaction. It is a method call dressed up as messaging. That has consequences — a slow or throwing listener blocks the publisher, and a listener's work commits or rolls back with the publisher's transaction. You opt into other behavior explicitly: @Async @EventListener moves a listener to another thread, and @TransactionalEventListener ties it to transaction lifecycle phases like AFTER_COMMIT.

The boundary to keep clear: these events never leave the JVM. They are not Kafka, not RabbitMQ, not durable — if the process dies mid-handling, the event is gone. Spring Modulith adds a persistent event publication registry on top to make cross-module delivery at-least-once, which is the closest Spring gets to a broker without one.

The questions below cover the listener API, sync vs async, transaction-bound phases, when to reach for a real broker instead, and how Spring Modulith turns events into reliable module boundaries.

Questions

5 in this topic