skip to content

Application Events

Publishing an event and consuming it with @EventListener decouples parts of one application, with conditional and async variants available. A common design question: when an in-process event is better than a direct method call.

part ofSpring Frameworkoverview, primer and where to startread it →
on this pageshow

questions

6

How do you publish and receive an application event in Spring?

level: juniorimportance: must knowfreq 70%

answer

  1. publishEvent on ApplicationEventPublisher
  2. @EventListener single-param method
  3. any POJO since 4.2
  4. synchronous, publisher thread, by default
  5. listener exception bubbles to publisher

basics

~10 s

Inject ApplicationEventPublisher and call publishEvent(event). Receive it with a method annotated @EventListener whose single parameter matches the event type. Spring calls that method when a matching event is published.

solid answer

~40 s

Spring has a built-in in-process publish/subscribe mechanism. To publish, inject ApplicationEventPublisher (any bean can, since it is aware of the context) and call publishEvent(someObject). To listen, annotate a bean method with @EventListener and declare one parameter of the event type; Spring routes matching events to it. Since Spring 4.2 the event can be any POJO — you no longer need to extend the old ApplicationEvent base class. Listeners run synchronously in the publisher's thread by default, so publishEvent returns only after all listeners finish. This decouples the producer from consumers: the publisher does not know who listens. In KataJob, UserRegistered is published this way, even though no consumer exists yet.

code

java · 24 lines
java
// Publisher
@Service
class RegistrationService {
    private final ApplicationEventPublisher publisher;
    RegistrationService(ApplicationEventPublisher publisher) {
        this.publisher = publisher;
    }
    void register(String email) {
        // ...persist the user...
        publisher.publishEvent(new UserRegistered(email)); // plain POJO event
    }
}

record UserRegistered(String email) {}

// Listener
@Component
class WelcomeMailer {
    @EventListener
    void on(UserRegistered event) {
        // runs synchronously on the caller's thread
        System.out.println("Welcome " + event.email());
    }
}

go deeper

for a junior

Know the two moving parts: publishEvent to send, @EventListener to receive.

for a middle

Add that it's synchronous by default and events can be plain POJOs.

for a senior

Discuss exception propagation aborting the publisher and decoupling within a modular monolith.

for a principal

Frame events as an in-process, unpersisted decoupling tool and know its failure/ordering semantics.

## What application events are Spring's ApplicationContext is also an event bus. It lets one bean announce that something happened without knowing who (if anyone) reacts. This is the Observer / publish-subscribe pattern, built into the container. ## Publishing Inject `org.springframework.context.ApplicationEventPublisher` into any bean and call `publishEvent(Object event)`: ```java @Service class RegistrationService { private final ApplicationEventPublisher publisher; RegistrationService(ApplicationEventPublisher publisher) { this.publisher = publisher; } void register(User u) { // ... persist ... publisher.publishEvent(new UserRegistered(u.getId())); } } ``` The `ApplicationContext` itself implements `ApplicationEventPublisher`, so you can also autowire the context, but injecting the narrower `ApplicationEventPublisher` interface is cleaner. ## The event object - **Legacy style:** extend `org.springframework.context.ApplicationEvent` (requires a source constructor arg). - **Modern style (Spring 4.2+):** the event can be **any object** — a plain POJO/record. Spring internally wraps non-ApplicationEvent payloads in a `PayloadApplicationEvent<T>`, but you rarely see that. ## Listening Annotate a bean method with `@org.springframework.context.event.EventListener` and give it exactly one parameter of the event type: ```java @Component class WelcomeMailer { @EventListener void on(UserRegistered e) { /* send email */ } } ``` The method may have any name, any visibility (Spring uses reflection), and may return `void` or another event object (returning an event re-publishes it). You can also match by supertype/interface: a listener for `Object` receives every event. The older alternative is implementing the `ApplicationListener<E extends ApplicationEvent>` interface with an `onApplicationEvent` method — still valid, but `@EventListener` is the idiomatic modern choice because it needs no interface and supports SpEL conditions. ## Execution model - **Synchronous by default.** `publishEvent` does not return until every matching listener has run, on the **publisher's thread**. - **Ordered** among listeners via `@Order` / `Ordered`, otherwise unspecified. - **Exceptions propagate.** A listener throwing an exception (in sync mode) aborts the remaining listeners and bubbles up to the caller of `publishEvent`. So a failed listener can break the publisher's flow — a common surprise. ## When to use Use events to decouple modules inside one process: the producer emits a fact, consumers react. In a Spring Modulith app like KataJob, events are the sanctioned way for one module to notify others without a compile-time dependency. Do not use them as a substitute for a direct method call when you actually need a synchronous return value or strong coupling. ## Gotchas - Nothing is persisted or retried — this is an in-memory bus; if the JVM dies, in-flight events are lost. - Having zero listeners is legal and silent (like KataJob's UserRegistered). - Because it is synchronous, a slow listener slows the publisher.

  • Do you have to extend ApplicationEvent for your event class?
    No. Since Spring 4.2 any object works as an event; Spring wraps it in a PayloadApplicationEvent internally. Extending ApplicationEvent is the older style and optional.
  • On which thread does a listener run by default?
    The publisher's thread — event delivery is synchronous, so publishEvent blocks until all listeners complete unless you make them @Async.

saying these in an interview costs you the question

  • Thinking every event class must extend ApplicationEvent
  • Assuming listeners run asynchronously by default
  • Believing a listener exception is swallowed and never affects the publisher

context

open as a page

How does the condition attribute on @EventListener work, and how do you filter or chain events?

level: middleimportance: should knowfreq 35%

basics

~10 s

@EventListener(condition = "...SpEL...") evaluates a SpEL expression against the event; the listener runs only if it returns true. You reference the event via #root.event or its payload fields via #propertyName / the special #root.args.

open as a page

What are payload events, and how does Spring handle a plain object published as an event?

level: middleimportance: should knowfreq 45%

basics

~10 s

A payload event is any plain object you publish without extending ApplicationEvent. Spring wraps it in a PayloadApplicationEvent<T> internally, but your @EventListener method just declares the payload type as its parameter.

open as a page

How do you make Spring application events asynchronous, and what changes about error handling and ordering?

level: seniorimportance: should knowfreq 50%

basics

~10 s

Add @Async to the @EventListener method (with @EnableAsync), or give the ApplicationEventMulticaster a TaskExecutor. Then listeners run on background threads, publishEvent no longer blocks, and listener exceptions no longer reach the publisher.

open as a page

At a high level, what is @TransactionalEventListener and why would you use it instead of @EventListener?

level: seniorimportance: should knowfreq 55%

basics

~20 s

@TransactionalEventListener is a specialized @EventListener that runs a listener tied to a transaction's outcome — by default only after the transaction commits — so side effects happen only when the data actually persisted. A plain @EventListener runs immediately, before commit.

open as a page

When would you choose Spring application events over a direct method call, and what are their limits?

level: principalimportance: should knowfreq 40%

basics

~20 s

Use events to decouple modules in one JVM: the producer emits a fact and any number of consumers react, with no compile-time dependency. Limits: in-process only, not persisted or retried, synchronous by default, and no delivery guarantee across crashes.

open as a page