Working with Events and Listeners

1. Creating Custom Application Events

Example: POJO event and legacy ApplicationEvent

// Modern: any POJO works
public record OrderPlaced(Long orderId, BigDecimal total, Instant when) {}

// Legacy: extend ApplicationEvent
public class LegacyEvent extends ApplicationEvent {
  public LegacyEvent(Object source) { super(source); }
}

2. Publishing Events (ApplicationEventPublisher)

Example: Publish domain event after save

@Service
public class OrderService {
  private final ApplicationEventPublisher publisher;
  public OrderService(ApplicationEventPublisher publisher) { this.publisher = publisher; }
  public void place(NewOrder o) {
    var saved = repo.save(Order.from(o));
    publisher.publishEvent(new OrderPlaced(saved.getId(), saved.getTotal(), Instant.now()));
  }
}

3. Creating Event Listeners (@EventListener)

Example: Listen for domain event

@Component
public class OrderListeners {
  @EventListener
  public void onPlaced(OrderPlaced evt) { /* react */ }
}
Attribute Use
classes Listen to multiple types
condition SpEL filter
id For ApplicationEventMulticaster lookup

4. Using Conditional Event Listening

Example: Conditional event listener with SpEL

@EventListener(condition = "#evt.total > 1000")
public void highValueOrder(OrderPlaced evt) { /* premium handling */ }

5. Implementing Async Event Handling

Example: Async event handler

@Async
@EventListener
public void asyncHandler(OrderPlaced evt) { /* runs on TaskExecutor */ }
Note: Requires @EnableAsync; publisher returns immediately.

6. Ordering Event Listeners (@Order)

Annotation Effect
@Order(1) Lowest = earliest
@Order(Ordered.HIGHEST_PRECEDENCE) Run first
Default Lowest precedence (last)

7. Using Transaction-Bound Events (@TransactionalEventListener)

Example: Post-commit transactional event listener

@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void afterCommit(OrderPlaced evt) { /* publish to MQ */ }
Phase Fired
BEFORE_COMMIT Pre-commit
AFTER_COMMIT (default) Post-commit success
AFTER_ROLLBACK On rollback
AFTER_COMPLETION Either commit or rollback

8. Listening to Built-in Spring Events

Event When
ContextRefreshedEvent Context refreshed
ContextStartedEvent start() called
ContextStoppedEvent stop() called
ContextClosedEvent close() called
ServletWebServerInitializedEvent Web server bound
HttpSessionCreatedEvent Session created

9. Creating Generic Events (ResolvableTypeProvider)

Example: Generic typed event with ResolvableType

public class TypedEvent<T> implements ResolvableTypeProvider {
  private final T payload;
  public TypedEvent(T payload) { this.payload = payload; }
  public T payload() { return payload; }
  @Override public ResolvableType getResolvableType() {
    return ResolvableType.forClassWithGenerics(getClass(), ResolvableType.forInstance(payload));
  }
}

@EventListener
public void onUserEvent(TypedEvent<User> e) { /* only User events */ }

10. Testing Event Publishing and Handling

Example: Assert published events in test

@SpringBootTest
@RecordApplicationEvents
class OrderServiceTests {
  @Autowired ApplicationEvents events;
  @Autowired OrderService svc;
  @Test void publishes() {
    svc.place(new NewOrder(/*…*/));
    assertEquals(1, events.stream(OrderPlaced.class).count());
  }
}