Backend Development 12 min read

Mastering Spring Event: Avoid Pitfalls and Ensure Reliable Publish‑Subscribe

This article shares hard‑won lessons from production incidents and provides practical guidelines—graceful shutdown, proper startup timing, suitable business scenarios, reliability patterns, and idempotent handling—to use Spring Event safely and effectively in Java backend systems.

macrozheng
macrozheng
macrozheng
Mastering Spring Event: Avoid Pitfalls and Ensure Reliable Publish‑Subscribe

Spring Event implements an event‑driven publish‑subscribe mechanism where developers can define custom events and listeners using

ApplicationListener

or the

@EventListener

annotation.

1. Graceful shutdown is mandatory before using Spring Event

During shutdown Spring searches for listeners in the

ApplicationContext

. The context cannot retrieve beans while it is closing; attempting to call

getBean

results in the error “Do not request a bean from a BeanFactory in a destroy method implementation”. In high‑traffic services, pending requests during shutdown cause Spring Event broadcasting to fail, leading to lost events.

Therefore, before publishing events you must cut off inbound traffic (HTTP, MQ, RPC) and only then close the Spring context.

This knowledge came from a real production failure and is critical to avoid similar bugs.

2. Event loss can occur during application startup

In our case a Kafka consumer started in the

init-method

phase published events before the

@EventListener

beans were registered, so the events were not delivered.

Solution: register services that open traffic only after the Spring context is fully initialized, e.g., in a

SmartLifecycle

implementation or by listening to

ContextRefreshedEvent

.

Best practice: delay opening HTTP, MQ, RPC traffic until Spring startup completes.

What business characteristics fit the publish‑subscribe model?

Publishers do not need to know how events are processed.

Publishers do not depend on processing results.

Multiple subscribers can handle events synchronously or asynchronously.

Subscribers are independent and do not rely on each other.

The model is unsuitable for strong‑consistency scenarios where a failure must roll back the whole transaction.

3. Strong‑consistency scenarios are not suitable for Spring Event

Examples such as order placement require inventory deduction and order creation to be atomic. If a subscriber fails, Spring Event cannot automatically trigger a rollback, making it unsafe for these cases.

4. Eventual‑consistency scenarios are ideal for Spring Event

After an order is successfully placed, publishing MQ messages, releasing locks, or notifying downstream systems can be handled by Spring Event because the core transaction has already succeeded; any subsequent failures can be retried without affecting the original order.

5. Ensure reliability with additional guarantees

Publishing with

applicationContext.publishEvent(event)

executes listeners sequentially; if a listener throws an exception, the method propagates the error, allowing the publisher to detect failures.

Three reliability strategies:

Subscriber‑side retry (e.g.,

@Retryable

from

spring-retry

).

Kafka consumer group retry by returning a failure to Kafka, which will redeliver the message.

Report unrecoverable failures to a fault‑management platform for manual intervention.

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

6. Subscribers must be idempotent

Because retries re‑execute all listeners, each listener must handle duplicate executions safely to avoid data inconsistency.

Why use Spring Event even when MQ exists?

MQ excels at inter‑service communication, while Spring Event is lightweight for intra‑application decoupling. Both can coexist: use MQ for cross‑service events and Spring Event for internal, fine‑grained coordination.

backendJavaSpringReliabilityeventidempotencePublish-Subscribe
macrozheng
Written by

macrozheng

Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.

0 followers
Reader feedback

How this landed with the community

login 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.