How do you publish and receive an application event in Spring?
answer
- publishEvent on ApplicationEventPublisher
- @EventListener single-param method
- any POJO since 4.2
- synchronous, publisher thread, by default
- listener exception bubbles to publisher
basics
~10 sInject 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 sSpring 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// 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
Know the two moving parts: publishEvent to send, @EventListener to receive.
Add that it's synchronous by default and events can be plain POJOs.
Discuss exception propagation aborting the publisher and decoupling within a modular monolith.
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