Why Graceful Shutdown Matters Before Using Spring Event (and How to Avoid Common Pitfalls)

This article explains why services must be gracefully shut down before leveraging Spring Event, how startup timing can cause event loss, which consistency scenarios suit or reject publish‑subscribe, and practical reliability techniques such as retries, idempotence, and fault reporting.

Architect's Guide
Architect's Guide
Architect's Guide
Why Graceful Shutdown Matters Before Using Spring Event (and How to Avoid Common Pitfalls)

1. Why graceful shutdown must be implemented before using Spring Event?

When Spring broadcasts an event it looks up all listeners in the ApplicationContext. During the context shutdown phase, calling getBean is prohibited and throws an error (e.g., "Do not request a bean from a BeanFactory in a destroy method implementation"). In high‑traffic systems, some requests may still arrive during shutdown, causing Spring Event to fail and raise exceptions. Therefore, before shutting down the Spring context you must first cut off inbound traffic (HTTP, MQ, RPC) to ensure no new work is submitted.

2. Why events can be lost during the service startup phase?

In the reported case a Kafka consumer started in the init-method phase, but the @EventListener beans were registered later, also in init-method. Because the listener registration lagged behind the consumer start, events published by the consumer found no listeners and were lost. The fix is to open inbound traffic only after Spring has fully started, for example by using SmartLifecycle or listening to ContextRefreshedEvent to register services.

3. Strong‑consistency scenarios are unsuitable for publish‑subscribe

In order‑placement workflows, inventory deduction and order confirmation must succeed or fail together. If a listener (e.g., inventory service) fails, Spring Event cannot roll back the whole transaction because each subscriber runs independently and exceptions are not propagated to the publisher. Implementing a compensating transaction across multiple listeners would be complex and error‑prone.

4. Eventual‑consistency scenarios fit publish‑subscribe

When the primary business action (e.g., order creation) has already succeeded, subsequent tasks such as releasing locks, sending MQ messages, or notifying downstream systems can be handled asynchronously via Spring Event. These follow‑up actions only need to succeed eventually; failures can be retried without affecting the original transaction. Example code uses @EventListener to handle events like order fulfillment, refund completion, or order expiration.

5. Ensure reliability when using Spring Event

Because Spring Event does not guarantee delivery or rollback, you must add reliability mechanisms:

Subscriber‑side retry : Use Spring Retry (e.g., @Retryable) so a failing listener automatically retries up to a configured number of attempts.

Kafka consumer‑group retry : If the event originates from a Kafka consumer, let Kafka handle retries by returning a consumption failure.

Report failures to a fault‑management platform : After exceeding retry limits, publish a fault message to a dedicated MQ that a monitoring system consumes, alerts developers, and provides a manual retry button.

@Retryable(value = Exception.class, maxAttempts = 3, backoff = @Backoff(delay = 100L, multiplier = 2))
public void performSuccess(PerformEvent event) {
    // business logic
}
<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
    <version>1.2.4.RELEASE</version>
</dependency>

6. Subscribers must be idempotent

When retries occur, all listeners are invoked again, so each listener's logic must be idempotent to avoid duplicate side effects and data inconsistency.

7. Why use Spring Event even when MQ exists?

Message queues excel at decoupling separate services and handling cross‑service communication, while Spring Event is lightweight and ideal for intra‑application communication. They are complementary: use MQ for inter‑service events and Spring Event for internal, fine‑grained decoupling.

Source: https://juejin.cn/post/7281159113882468371

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaretryIdempotenceSpring EventEvent‑Driven
Architect's Guide
Written by

Architect's Guide

Dedicated to sharing programmer-architect skills—Java backend, system, microservice, and distributed architectures—to help you become a senior architect.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.