Course topics

By WebNest Studio Engineering Team

Spring Framework Tutorial

Spring Application Events

Spring includes a built-in publish-subscribe event mechanism that lets beans communicate without being directly coupled to one another. One part of the application publishes an event describing something that happened — an order was placed, a user registered — and any number of other beans can listen for that event and react, without the publisher knowing or caring who is listening.

This is built around three pieces: an event class extending ApplicationEvent (or, since Spring 4.2, any arbitrary POJO), the ApplicationEventPublisher interface used to publish events, and listeners that either implement ApplicationListener<T> or, more commonly today, use the @EventListener annotation on a plain method.

Publishing Events

Any bean can publish an event by having an ApplicationEventPublisher injected (ApplicationContext itself implements this interface) and calling publishEvent(event). By default, event delivery to listeners is synchronous and happens on the same thread as the publisher, meaning the publish() call blocks until every matching listener has finished handling the event — unless a listener is explicitly marked @Async.

Listening with @EventListener

The simplest way to listen for an event is annotating a method with @EventListener and typing its single parameter as the event class. Spring automatically registers this method as a listener for events of that exact type (and subtypes). This avoids implementing the more verbose ApplicationListener<T> interface, though that interface remains a valid, type-safe alternative that some codebases still prefer.

  • @EventListener on a method — modern, concise, supports conditional matching via SpEL (condition = "...").
  • ApplicationListener<MyEvent> implemented on a bean — the original, interface-based approach.
  • Custom events since Spring 4.2 no longer need to extend ApplicationEvent — any object can be published and listened for.

Built-in Framework Events

Spring publishes several of its own lifecycle events that application code can listen for, including ContextRefreshedEvent (fired once the ApplicationContext is fully initialized), ContextClosedEvent (fired on shutdown), and ContextStartedEvent/ContextStoppedEvent. Listening for ContextRefreshedEvent is a common way to run startup logic only once the entire bean graph is guaranteed to be ready.

Example

Publishing a custom OrderPlacedEvent and reacting to it with @EventListener

Java
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

class OrderPlacedEvent {
    final String orderId;
    OrderPlacedEvent(String orderId) { this.orderId = orderId; }
}

@Component
class OrderService {
    private final ApplicationEventPublisher publisher;
    OrderService(ApplicationEventPublisher publisher) { this.publisher = publisher; }

    void placeOrder(String orderId) {
        System.out.println("Order placed: " + orderId);
        publisher.publishEvent(new OrderPlacedEvent(orderId));
    }
}

@Component
class InventoryListener {
    @EventListener
    public void onOrderPlaced(OrderPlacedEvent event) {
        System.out.println("Inventory: reserving stock for " + event.orderId);
    }
}

@Configuration
@ComponentScan(basePackages = "com.webnest.events")
class AppConfig { }

public class EventDemo {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        context.getBean(OrderService.class).placeOrder("ORD-2001");
    }
}
Output
Order placed: ORD-2001
Inventory: reserving stock for ORD-2001

Common Mistakes

  • Assuming event listeners run asynchronously by default — without @Async, publishEvent() blocks until every listener finishes.
  • Publishing an event before all listener beans exist (e.g. too early in the startup sequence), so some listeners never receive it.
  • Letting an exception thrown inside one @EventListener silently abort delivery to other listeners, since synchronous publication propagates exceptions back to the publisher.
  • Overusing the event system for simple direct method calls between two tightly related beans, adding indirection where a plain method call would be clearer.

Exercise

A teammate is about to make this mistake: Assuming event listeners run asynchronously by default — without @Async, publishEvent() blocks until every listener finishes. Use Publishing Events to review the decision. Write a corrected implementation or design sketch that follows Events decouple publishers from listeners: the publisher does not know who (if anyone) is listening., then name one edge case you would check.

Show hints
  • Start with Publishing Events and compare your approach with Publishing a custom OrderPlacedEvent and reacting to it with @EventListener. Identify the decision or behavior the lesson recommends.
  • Walk through your scenario step by step. Confirm that it follows Events decouple publishers from listeners: the publisher does not know who (if anyone) is listening. and that it would catch the failure described by Assuming event listeners run asynchronously by default — without @Async, publishEvent() blocks until every listener finishes..

Quick quiz

1. Which choice best describes the recommended approach in Spring Application Events?

  • A. Assuming event listeners run asynchronously by default — without @Async, publishEvent() blocks until every listener finishes.
  • B. Publishing an event before all listener beans exist (e.g. too early in the startup sequence), so some listeners never receive it.
  • C. Events decouple publishers from listeners: the publisher does not know who (if anyone) is listening.
Show answer

C. This is the recommended approach: Events decouple publishers from listeners: the publisher does not know who (if anyone) is listening. Avoid: Assuming event listeners run asynchronously by default — without @Async, publishEvent() blocks until every listener finishes.

Interview questions

Why does this principle matter in Spring Application Events: Events decouple publishers from listeners: the publisher does not know who (if anyone) is listening.

Show answer

Events decouple publishers from listeners: the publisher does not know who (if anyone) is listening. Apply this in Publishing Events; the example Publishing a custom OrderPlacedEvent and reacting to it with @EventListener shows one way to do it. Avoid Assuming event listeners run asynchronously by default — without @Async, publishEvent() blocks until every listener finishes. and explain how your implementation or design prevents that failure.

Key Points to Remember

  • Events decouple publishers from listeners: the publisher does not know who (if anyone) is listening.
  • @EventListener on a method is the modern way to subscribe; ApplicationListener<T> is the older interface-based way.
  • Event delivery is synchronous and on the same thread by default, unless the listener is marked @Async.
  • Since Spring 4.2, any POJO can be an event — extending ApplicationEvent is no longer required.

Practice in the playground

Write and run your own code for this topic, or start from the exercise or an example. Runs plain Java 17. Frameworks such as Spring, and databases, are not available here. Your code for each topic is kept in this browser.

Run the examples

Change an input, predict the result, then compare it with the output. Explain why the result changes.

These examples need a JDK project with their libraries on your computer, so they can be copied but not run here. Codelab runs Python, FastAPI backends with SQLite, plain Java 17 and HTML/CSS/JavaScript.

WebNest Studio

Written by

WebNest Studio Engineering Team

Practical lessons from developers working with Java, Python, React, databases and production systems.

Led by Vansh Duggal, Founder, WebNest Studio · Full-stack & software engineering