Applying Spring Transaction Hooks for Asynchronous Kafka Message Publishing

This article explains how to use Spring's TransactionSynchronizationManager to detect active transactions and register synchronization callbacks so that Kafka messages are sent asynchronously after transaction commit, illustrated with a payment‑system logging scenario and complete Java code examples.

Code Ape Tech Column
Code Ape Tech Column
Code Ape Tech Column
Applying Spring Transaction Hooks for Asynchronous Kafka Message Publishing

The author introduces a "secret skill"—Spring transaction hook functions—and demonstrates their correct usage through a real‑world payment‑system case where account flow records must be archived by sending messages to Kafka.

1. Case Background

In a payment system, each account's fund flow must be recorded and archived. The CTO requires that when a flow is written, the related information be pushed as a Kafka message for a separate archiving service to store, ensuring the archiving system has exclusive write permission.

The CTO suggests building a second‑party library that encapsulates the Kafka‑sending logic, keeping integration simple for other business units.

2. Solution Design

The library should:

1. Use Spring Boot and be provided as a starter. 2. Create its own Kafka producer instead of using Spring's KafkaTemplate to avoid conflicts. 3. Offer a simple API with low learning cost. 4. Ensure the message‑sending operation participates in the surrounding transaction without affecting the main business flow.

Point 4 is critical: the message should be sent asynchronously and only after the transaction commits.

3. TransactionSynchronizationManager in Action

The class is a static utility that acts as a transaction synchronizer. The following pseudo‑code shows how to implement the required logic.

private final ExecutorService executor = Executors.newSingleThreadExecutor();

public void sendLog() {
    // Check if a transaction is active
    if (!TransactionSynchronizationManager.isSynchronizationActive()) {
        // No transaction: send message to Kafka asynchronously
        executor.submit(() -> {
            try {
                // send to Kafka
            } catch (Exception e) {
                // log/handle exception
            }
        });
        return;
    }
    // Transaction is active: register a synchronization callback
    TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
        @Override
        public void afterCompletion(int status) {
            if (status == TransactionSynchronization.STATUS_COMMITTED) {
                // After commit, send message asynchronously
                executor.submit(() -> {
                    try {
                        // send to Kafka
                    } catch (Exception e) {
                        // log/handle exception
                    }
                });
            }
        }
    });
}

The key methods are TransactionSynchronizationManager.isSynchronizationActive() to detect an active transaction and registerSynchronization() to add a callback that runs after commit.

3.1 Determining Transaction Presence

The source of isSynchronizationActive() shows it checks a ThreadLocal set that is populated when Spring starts transaction synchronization:

private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =
        new NamedThreadLocal<>("Transaction synchronizations");

public static boolean isSynchronizationActive() {
    return (synchronizations.get() != null);
}

Spring's transaction manager calls TransactionSynchronizationManager.initSynchronization() at transaction start, inserting a LinkedHashSet into the ThreadLocal.

3.2 Triggering Custom Logic After Commit

By registering a TransactionSynchronizationAdapter and overriding afterCompletion(int status), custom code runs only when status == TransactionSynchronization.STATUS_COMMITTED. This mechanism leverages Spring's internal invokeAfterCommit/invokeAfterCompletion callbacks.

4. Summary

To safely send Kafka messages only after a Spring transaction commits, use TransactionSynchronizationManager.isSynchronizationActive() to detect the transaction, and registerSynchronization() to attach a callback that performs asynchronous message publishing after commit. Ensure the code runs in the same thread context to avoid missing the synchronization.

Backend Technical Community Invitation

Join our backend‑focused technical group to share knowledge, recruitment info, and internal referrals.

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.

BackendJavatransactionspringAsynchronousKafka
Code Ape Tech Column
Written by

Code Ape Tech Column

Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn

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.