Spring Events (@EventListener, ApplicationEventPublisher)
Fast recall
Quick revision
Core answer
ApplicationEvent). For post-commit side effects (emails, notifications), use `@TransactionalEventListener` with a phase like AFTER_COMMIT.Knowledge of decoupled communication patterns within a Spring application and how to avoid direct coupling between services.
Not knowing that events are synchronous by default — a slow listener blocks the publisher — or missing that a transaction-committed event requires @TransactionalEventListener.
Explain @TransactionalEventListener with its transaction phases (AFTER_COMMIT, AFTER_ROLLBACK, BEFORE_COMMIT) for reliable post-transaction actions like sending emails only after a successful save.
Interview-ready guide
Interview answer
Spring's event system implements the Observer pattern within the container. A service publishes an event; listeners react without any direct compile-time coupling. But the most important thing to understand is what decoupling does and does not mean here — and when Spring Events are the wrong tool entirely.
Events are synchronous by default
publishEvent(event)runs every matching@EventListenermethod synchronously on the same thread- Events decouple the compile-time dependency — the publisher does not import the listener — but they do not decouple execution at runtime
- A slow listener blocks the publisher; an exception in a listener propagates back and rolls back the publisher's transaction
`@TransactionalEventListener` — for post-commit actions
- The production pattern for notifications, emails, and cache updates that must only happen after a successful commit
@TransactionalEventListener(phase = AFTER_COMMIT)— the listener fires only if the transaction committed; on rollback, it is skipped- Trap: if no transaction is active when the event is published, the listener never fires silently; add
fallbackExecution = trueif you need the listener to fire outside a transaction context - Four phases cover the lifecycle:
BEFORE_COMMIT(add work to the active transaction — outbox pattern writes here),AFTER_COMMIT(default for side effects),AFTER_ROLLBACK(compensating action),AFTER_COMPLETION(cleanup either way)
True async decoupling
- Add
@Asyncto the listener method — runs on a separate thread pool, fully decoupling execution timing - Requires
@EnableAsyncand an explicitThreadPoolTaskExecutorwith bounded core/max pool sizes and a bounded queue - Without a custom executor,
SimpleAsyncTaskExecutorcreates an unbounded number of threads and will take the JVM down under a traffic spike
Ordering and generics
- Multiple listeners on the same event type fire in undefined order without
@Order(n)— classic dev-vs-prod reproducibility bug after a Spring upgrade - Generic events since 4.2 let one listener distinguish
OrderEventfromOrderEventwithout runtime type checks; Spring resolves type parameters at registration time
When to reach for a real broker instead
- Spring Events are in-process, single-JVM, best-effort — they do not survive a crash, do not cross service boundaries, and give no at-least-once guarantee
- For cross-service events, durable retry, or replay, use Kafka, RabbitMQ, or a transactional outbox with CDC
- Spring Events are the right tool for decoupling inside a service; wrong tool for decoupling between services or for workflows that must survive a pod restart
Events since 4.2 are plain POJOs — no need to extend ApplicationEvent. Publish any object; any @EventListener method that accepts that type is invoked.
Spring Events (@EventListener, ApplicationEventPublisher)
Direct Answer
Spring's event system implements the Observer pattern within the container. A component publishes an event by calling ApplicationEventPublisher.publishEvent(), and any method annotated with @EventListener that accepts that event type is invoked synchronously in the same thread, in the same transaction, by default. Listeners can be made async with @Async (separate thread pool, full execution decoupling), and ordered with @Order. Since Spring 4.2, events can be plain POJOs (no need to extend ApplicationEvent). For post-commit side effects (emails, notifications), use @TransactionalEventListener with a phase like AFTER_COMMIT.
Common Mistakes & Pitfalls
Not knowing that events are synchronous by default — a slow listener blocks the publisher — or missing that a transaction-committed event requires @TransactionalEventListener.
Check your understanding
Answer these without looking back at the article.
- Why would you use @TransactionalEventListener instead of publishing the event after the transaction commits manually?
- What happens to an @Async @EventListener if the thread pool is exhausted?
- How do Spring events compare to an in-process message bus like Guava EventBus?
- How do you propagate the security context to an @Async @EventListener?
Related interview prep categories
1Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.