Mastering Spring Boot 3 TransactionExecutionListener with Real‑World Code
This article introduces Spring Boot 3's TransactionExecutionListener, explains its purpose compared to traditional transaction hooks, provides a complete code example—including entity, service, custom listener, and test cases—and demonstrates how to observe transaction phases and handle both successful commits and rollbacks.
1. Introduction
Traditional Spring transaction hooks such as TransactionSynchronizationManager#registerSynchronization and @TransactionalEventListener allow callbacks at various transaction stages. Starting with Spring 6.1, the framework adds TransactionExecutionListener, a stateless interface for observing transaction creation and completion, mainly for monitoring and statistics. For resource‑management tasks that require state, the classic TransactionSynchronization approach is still recommended.
2. Practical Example
2.1 Interface Overview
TransactionExecutionListenerdefines callbacks before and after each transaction step (begin, commit, rollback). The TransactionExecution argument represents the current transaction—either a TransactionStatus for a PlatformTransactionManager or a ReactiveTransaction for a reactive manager.
public interface TransactionExecutionListener {
default void beforeBegin(TransactionExecution transaction) {}
default void afterBegin(TransactionExecution transaction, @Nullable Throwable beginFailure) {}
default void beforeCommit(TransactionExecution transaction) {}
default void afterCommit(TransactionExecution transaction, @Nullable Throwable commitFailure) {}
default void beforeRollback(TransactionExecution transaction) {}
default void afterRollback(TransactionExecution transaction, @Nullable Throwable rollbackFailure) {}
}2.2 Entity Definition
@Entity
@Table(name = "t_account")
public class Account {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private BigDecimal amount;
}2.3 Service Method
@Transactional
public Account save(Account account) {
return this.accountRepository.saveAndFlush(account);
}2.4 Custom Listener Implementation
@Component
public class PackTransactionExecutionListener implements TransactionExecutionListener {
@Override
public void beforeBegin(TransactionExecution transaction) {
System.out.printf("准备开始事务之前beforeBegin, 事务名: %s, 是否只读: %s, 是否嵌套: %s%n",
transaction.getTransactionName(),
transaction.isReadOnly(),
transaction.isNested());
}
@Override
public void afterBegin(TransactionExecution transaction, Throwable beginFailure) {
System.err.printf("准备事务之后afterBegin - %s, 事务名: %s, 是否只读: %s, 是否嵌套: %s, 异常信息: %s%n",
beginFailure == null ? "成功" : "失败",
transaction.getTransactionName(),
transaction.isReadOnly(),
transaction.isNested(),
beginFailure == null ? null : beginFailure.getMessage());
}
@Override
public void beforeCommit(TransactionExecution transaction) {
System.out.printf("提交事务之前beforeCommit, 事务名: %s, 是否只读: %s, 是否嵌套: %s, 是否回滚: %s%n",
transaction.getTransactionName(),
transaction.isReadOnly(),
transaction.isNested(),
transaction.isRollbackOnly());
}
@Override
public void afterCommit(TransactionExecution transaction, Throwable commitFailure) {
System.err.printf("提交事务之后afterCommit - %s, 事务名: %s, 是否只读: %s, 是否嵌套: %s, 是否回滚: %s, 异常信息: %s%n",
commitFailure == null ? "成功" : "失败",
transaction.getTransactionName(),
transaction.isReadOnly(),
transaction.isNested(),
transaction.isRollbackOnly(),
commitFailure == null ? null : commitFailure.getMessage());
}
@Override
public void beforeRollback(TransactionExecution transaction) {
System.out.printf("事务回滚之前beforeRollback, 事务名: %s, 是否只读: %s, 是否嵌套: %s, 是否回滚: %s%n",
transaction.getTransactionName(),
transaction.isReadOnly(),
transaction.isNested(),
transaction.isRollbackOnly());
}
@Override
public void afterRollback(TransactionExecution transaction, Throwable rollbackFailure) {
System.err.printf("事务回滚之后afterRollback - %s, 事务名: %s, 是否只读: %s, 是否嵌套: %s, 是否回滚: %s, 异常信息: %s%n",
rollbackFailure == null ? "成功" : "失败",
transaction.getTransactionName(),
transaction.isReadOnly(),
transaction.isNested(),
transaction.isRollbackOnly(),
rollbackFailure == null ? null : rollbackFailure.getMessage());
}
}2.5 Test Cases
Normal save operation produces the following console output, confirming that each callback is invoked and reports success.
准备开始事务之前beforeBegin, 事务名: com.pack.service.AccountService.save, 是否只读: false, 是否嵌套: false
准备事务之后afterBegin - 成功, 事务名: com.pack.service.AccountService.save, 是否只读: false, 是否嵌套: false, 异常信息: null
提交事务之前beforeCommit, 事务名: com.pack.service.AccountService.save, 是否只读: false, 是否嵌套: false, 是否回滚: false
提交事务之后afterCommit - 成功, 事务名: com.pack.service.AccountService.save, 是否只读: false, 是否嵌套: false, 是否回滚: false, 异常信息: nullWhen an exception (e.g., division by zero) is thrown inside the service method, the transaction rolls back and the listener logs rollback callbacks. The failure message shown is the business‑logic exception; the rollback itself does not generate an additional exception.
准备开始事务之前beforeBegin, 事务名: com.pack.service.AccountService.save, 是否只读: false, 是否嵌套: false
准备事务之后afterBegin - 成功, 事务名: com.pack.service.AccountService.save, 是否只读: false, 是否嵌套: false, 异常信息: null
事务回滚之前beforeRollback, 事务名: com.pack.service.AccountService.save, 是否只读: false, 是否嵌套: false, 是否回滚: false
事务回滚之后afterRollback - 成功, 事务名: com.pack.service.AccountService.save, 是否只读: false, 是否嵌套: false, 是否回滚: false, 异常信息: null3. Conclusion
The newly introduced TransactionExecutionListener offers developers fine‑grained, stateless visibility into each transaction phase, making it ideal for monitoring and statistical analysis. For scenarios that require stateful resource handling or detailed transaction‑bound logic, the traditional TransactionSynchronization mechanism remains the appropriate choice.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
