Spring Vertx 响应式事务,spring-tx-reactive-vertx
-
- 背景
- 介绍
- 代码演示
- 工程结构
- 框架实现
-
- 核心--事务管理器
-
- VertxTransactionManager
- AbstractVertxTransactionManager
- VertxJdbcPoolTransactionManager
- [VertxTransactionSynchronizationManager 事务同步器管理器](#VertxTransactionSynchronizationManager 事务同步器管理器)
- 基础--事务对象
-
- VertxTransaction
- GenericVertxTransaction
- [VertxTransactionContext 事务上下文](#VertxTransactionContext 事务上下文)
- VertxTransactionInfo
- VertxTransactionObject
- [VertxTransactionSynchronization 事务同步器](#VertxTransactionSynchronization 事务同步器)
- [VertxConnectionTransactionSynchronization 数据库VertxConnection同步器](#VertxConnectionTransactionSynchronization 数据库VertxConnection同步器)
- 核心工具类
- 核心Holder对象
-
- VertxConnectionHolder
- VertxTransactionContextHolder
- [TransactionManagerHolder 兼容性 事务管理器持有器](#TransactionManagerHolder 兼容性 事务管理器持有器)
- 核心--事务拦截器--入口
- 结束
背景
Spring 在响应式编程中支持事务管理,主要通过 Spring Data R2DBC。
国内主流的Mybatis,官方没有提供响应式实现。
如果是在Spring Reactive Streams的环境下构建一个Spring应用,那么Mybatis只能以阻塞式JDBC的方式集成在应用中,无法发挥响应式最大的优势。
前面我们已经实现了 mybatis-vertx-adaptor,已经让Mybatis支持响应式,现在需要将响应式Mybatis集成Spring事务管理。
介绍
Spring 提供了 ReactiveTransactionManager 响应式事务管理器。API 支持 Reactive Streams 环境下的 Mono / Flux。
Spring 提供 ReactiveAdapterRegistry,可以支持我们自定义的响应式对象适配,如Vertx Future 适配 Mono / Flux。
通过 ReactiveAdapterRegistry注册 VertxReactiveAdapter,可让ReactiveTransactionManager 支持 Future 返回值的DAO操作的事务管理。 (已验证失败,原因:无法正常地在Reactor Context 传递事务上下文)
为了扩展性及可维护性,需要实现一套支持Vertx的Spring TransactionManager,以无损无侵入性的把事务交给Spring来管理。
工程名:spring-tx-reactive-vertx
原理:实现一套支持Vertx的Spring TransactionManager。
用法:引入 spring-tx-reactive-vertx 依赖,@Transactional 申明式事务不需要任何改变,编程式事务则需要替换一些关键实现类。
代码演示
kotlin
@Transactional
override fun tx8(): Future<Void> {
return this.vertx.launchFutureVoid {
val synchronizationManager = VertxTransactionSynchronizationManager.forCurrentTransaction().await()
logger.info("{}, synchronizationManager={}", Thread.currentThread(), synchronizationManager)
val users = vertxUserMapper.listAll().await()
logger.info("users.size={}", users.size)
}
}
@Transactional
override fun tx9(): Future<Void> {
return this.vertx.launchFutureVoid {
var users = vertxUserMapper.listAll().await()
logger.info("before users.size={}", users.size)
val user = User().apply {
this.nick = "tx1-" + Random.nextInt()
}
vertxUserMapper.insert(user).coAwait()
users = vertxUserMapper.listAll().await()
logger.info("after users.size={}", users.size)
}
}
工程结构

- annotation:一些注解和ConfigurationSelector、AbstractTransactionManagementConfiguration,用于配置spring-tx-reactive-vertx,和支持Spring应用中支持多种TransactionManagement(PlatformTransactionManager、ReactiveTransactionManager、VertxTransactionManager)
- extension:Kotlin实现的扩展函数
- holder:事务管理中的各种Holder对象
- interceptor:扩展了Spring原生事务拦截器的VertxTransactionInterceptor
- manager:事务管理器相关实现
- transaction:事务相关的抽象对象
- util:相关工具类
框架实现
核心--事务管理器
VertxTransactionManager
实现TransactionManager接口,这是Spring的标记接口。
定义三个方法,根据当前定义获取事务对象、事务提交、事务回滚。
kotlin
package com.luomin.vertx.tx.manager
import com.luomin.vertx.tx.transaction.VertxTransaction
import io.vertx.core.Future
import org.springframework.transaction.TransactionDefinition
import org.springframework.transaction.TransactionManager
/**
* <br>description<br/>
*
* <br>@author luomin<br/>
* <br>2025/3/31 14:24<br/>
*/
interface VertxTransactionManager : TransactionManager {
fun getVertxTransaction(definition: TransactionDefinition?): Future<VertxTransaction>
fun commit(transaction: VertxTransaction): Future<Void>
fun rollback(transaction: VertxTransaction): Future<Void>
companion object {
const val beanName = "vertxTransactionManager"
}
}
AbstractVertxTransactionManager
实现 VertxTransactionManager 基础功能,支持事务传播特性,支持Vertx上下文,并提供模板方法,留给子类实现。
kotlin
package com.luomin.vertx.tx.manager
import com.luomin.vertx.tx.transaction.GenericVertxTransaction
import com.luomin.vertx.tx.transaction.VertxTransaction
import com.luomin.vertx.tx.transaction.VertxTransactionSynchronization
import com.luomin.vertx.tx.util.VertxTransactionSynchronizationUtils
import io.vertx.core.Future
import org.apache.commons.logging.LogFactory
import org.springframework.transaction.*
import java.util.concurrent.atomic.AtomicBoolean
import java.util.function.Predicate
/**
* <br>description<br/>
* @see org.springframework.transaction.reactive.AbstractReactiveTransactionManager
* @see org.springframework.transaction.support.AbstractPlatformTransactionManager
*
* <br>@author luomin<br/>
* <br>2025/3/31 14:24<br/>
*/
abstract class AbstractVertxTransactionManager : ConfigurableTransactionManager, VertxTransactionManager {
protected val logger = LogFactory.getLog(javaClass)
private var transactionExecutionListeners = mutableListOf<TransactionExecutionListener>()
override fun setTransactionExecutionListeners(listeners: Collection<TransactionExecutionListener>) {
this.transactionExecutionListeners = listeners as MutableList<TransactionExecutionListener>
}
override fun getTransactionExecutionListeners(): Collection<TransactionExecutionListener?> {
return this.transactionExecutionListeners
}
override fun getVertxTransaction(definition: TransactionDefinition?): Future<VertxTransaction> {
// Use defaults if no transaction definition given.
val def = definition ?: TransactionDefinition.withDefaults()
return VertxTransactionSynchronizationManager.forCurrentTransaction().flatMap { synchronizationManager ->
val transaction = this.doGetTransaction(synchronizationManager)
// Cache debug flag to avoid repeated checks.
val debugEnabled = logger.isDebugEnabled
if (this.isExistingTransaction(transaction)) {
// Existing transaction found -> check propagation behavior to find out how to behave.
return@flatMap handleExistingTransaction(synchronizationManager, def, transaction, debugEnabled)
}
// Check definition settings for new transaction.
if (def.timeout < TransactionDefinition.TIMEOUT_DEFAULT) {
return@flatMap Future.failedFuture(InvalidTimeoutException("Invalid transaction timeout", def.timeout))
}
// No existing transaction found -> check propagation behavior to find out how to proceed.
val propagationBehavior = def.propagationBehavior
if (propagationBehavior == TransactionDefinition.PROPAGATION_MANDATORY) {
return@flatMap Future.failedFuture(IllegalTransactionStateException("No existing transaction found for transaction marked with propagation 'mandatory'"))
} else if (propagationBehavior == TransactionDefinition.PROPAGATION_REQUIRED || propagationBehavior == TransactionDefinition.PROPAGATION_REQUIRES_NEW || propagationBehavior == TransactionDefinition.PROPAGATION_NESTED) {
return@flatMap VertxTransactionSynchronizationManager.forCurrentTransaction().flatMap { nestedSynchronizationManager ->
this.suspend(nestedSynchronizationManager, null).flatMap { suspendedResources ->
if (debugEnabled) {
logger.debug("Creating new transaction with name [" + def.name + "]: " + def)
}
val status = this.newVertxTransaction(nestedSynchronizationManager, def, transaction, true, false, debugEnabled, suspendedResources)
this.transactionExecutionListeners.forEach { it.beforeBegin(status) }
return@flatMap this.doBegin(nestedSynchronizationManager, transaction, def)
.onSuccess { this.prepareSynchronization(nestedSynchronizationManager, status, def) }
.onFailure { ex -> this.transactionExecutionListeners.forEach { it.afterBegin(status, ex) } }
.map { status as VertxTransaction }
.onSuccess { reStatus -> this.transactionExecutionListeners.forEach { it.afterBegin(reStatus, null) } }
.recover { ex ->
if (ErrorPredicates.RUNTIME_OR_ERROR.test(ex)) {
this.resume(nestedSynchronizationManager, null, suspendedResources)
}
return@recover Future.failedFuture<VertxTransaction>(ex)
}
}
}
} else {
// Create "empty" transaction: no actual transaction, but potentially synchronization.
if (def.isolationLevel != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled) {
logger.warn("Custom isolation level specified but no actual transaction initiated; isolation level will effectively be ignored: $def")
}
return@flatMap Future.succeededFuture(this.prepareVertxTransaction(synchronizationManager, def, null, true, debugEnabled, null))
}
}
}
override fun commit(transaction: VertxTransaction): Future<Void> {
if (transaction.isCompleted) {
return Future.failedFuture(IllegalTransactionStateException("Transaction is already completed - do not call commit or rollback more than once per transaction"))
}
return VertxTransactionSynchronizationManager.forCurrentTransaction().flatMap { synchronizationManager ->
val vertxTx = transaction as GenericVertxTransaction
if (vertxTx.isReadOnly) {
if (vertxTx.debug) {
logger.debug("Transactional code has requested rollback")
}
return@flatMap this.processRollback(synchronizationManager, vertxTx)
}
return@flatMap this.processCommit(synchronizationManager, vertxTx)
}
}
override fun rollback(transaction: VertxTransaction): Future<Void> {
if (transaction.isCompleted) {
return Future.failedFuture(IllegalTransactionStateException("Transaction is already completed - do not call commit or rollback more than once per transaction"))
}
return VertxTransactionSynchronizationManager.forCurrentTransaction().flatMap { synchronizationManager ->
val vertxTx = transaction as GenericVertxTransaction
return@flatMap this.processRollback(synchronizationManager, vertxTx)
}
}
open fun isExistingTransaction(transaction: Any): Boolean {
return false
}
abstract fun doGetTransaction(synchronizationManager: VertxTransactionSynchronizationManager): Any
abstract fun doBegin(synchronizationManager: VertxTransactionSynchronizationManager, transaction: Any, definition: TransactionDefinition): Future<Void>
open fun doSuspend(synchronizationManager: VertxTransactionSynchronizationManager, transaction: Any): Future<Any?> {
throw TransactionSuspensionNotSupportedException("Transaction manager [" + javaClass.getName() + "] does not support transaction suspension")
}
open fun doResume(synchronizationManager: VertxTransactionSynchronizationManager, transaction: Any?, suspendedResources: Any): Future<Void> {
throw TransactionSuspensionNotSupportedException("Transaction manager [" + javaClass.getName() + "] does not support transaction suspension")
}
abstract fun doCommit(synchronizationManager: VertxTransactionSynchronizationManager, status: GenericVertxTransaction): Future<Void>
open fun prepareForCommit(synchronizationManager: VertxTransactionSynchronizationManager, status: GenericVertxTransaction): Future<Void> {
return Future.succeededFuture()
}
abstract fun doRollback(synchronizationManager: VertxTransactionSynchronizationManager, status: GenericVertxTransaction): Future<Void>
open fun doSetRollbackOnly(synchronizationManager: VertxTransactionSynchronizationManager, status: GenericVertxTransaction): Future<Void> {
throw IllegalTransactionStateException("Participating in existing transactions is not supported - when 'isExistingTransaction' returns true, appropriate 'doSetRollbackOnly' behavior must be provided")
}
open fun doCleanupAfterCompletion(synchronizationManager: VertxTransactionSynchronizationManager, transaction: Any?): Future<Void> {
return Future.succeededFuture()
}
open fun registerAfterCompletionWithExistingTransaction(
synchronizationManager: VertxTransactionSynchronizationManager,
transaction: Any,
synchronizations: List<VertxTransactionSynchronization>
): Future<Void> {
logger.debug("Cannot register Spring after-completion synchronization with existing transaction - processing Spring after-completion callbacks immediately, with outcome status 'unknown'")
return this.invokeAfterCompletion(synchronizationManager, synchronizations, VertxTransactionSynchronizationManager.STATUS_UNKNOWN)
}
private fun handleExistingTransaction(
synchronizationManager: VertxTransactionSynchronizationManager,
definition: TransactionDefinition,
transaction: Any,
debugEnabled: Boolean
): Future<VertxTransaction> {
when (definition.propagationBehavior) {
TransactionDefinition.PROPAGATION_NEVER -> return Future.failedFuture(IllegalTransactionStateException("Existing transaction found for transaction marked with propagation 'never'"))
TransactionDefinition.PROPAGATION_NOT_SUPPORTED -> {
if (debugEnabled) {
logger.debug("Suspending current transaction")
}
val suspend = this.suspend(synchronizationManager, transaction)
return suspend.map { prepareVertxTransaction(synchronizationManager, definition, null, false, debugEnabled, it) }
}
TransactionDefinition.PROPAGATION_REQUIRES_NEW -> {
if (debugEnabled) {
logger.debug("Suspending current transaction, creating new transaction with name [" + definition.name + "]")
}
val suspend = this.suspend(synchronizationManager, transaction)
return suspend.flatMap { suspendedResourcesHolder ->
val status = this.newVertxTransaction(synchronizationManager, definition, transaction, true, false, debugEnabled, suspendedResourcesHolder)
this.transactionExecutionListeners.forEach { it.beforeBegin(status) }
return@flatMap this.doBegin(synchronizationManager, transaction, definition).compose {
this.prepareSynchronization(synchronizationManager, status, definition)
return@compose Future.succeededFuture(it)
}.recover { t ->
this.transactionExecutionListeners.forEach { it.afterBegin(status, t) }
return@recover Future.failedFuture(t)
}.recover { t ->
if (ErrorPredicates.RUNTIME_OR_ERROR.test(t)) {
return@recover this.resumeAfterBeginException(synchronizationManager, transaction, suspendedResourcesHolder, t).flatMap { Future.failedFuture(t) }
}
return@recover Future.failedFuture(t)
}.map { status }.compose {
this.transactionExecutionListeners.forEach { it.afterBegin(status, null) }
return@compose Future.succeededFuture(it)
}
}
}
TransactionDefinition.PROPAGATION_NESTED -> {
if (debugEnabled) {
logger.debug("Creating nested transaction with name [" + definition.name + "]")
}
// Nested transaction through nested begin and commit/rollback calls.
val status = this.newVertxTransaction(synchronizationManager, definition, transaction, true, true, debugEnabled, null)
return doBegin(synchronizationManager, transaction, definition).map {
this.prepareSynchronization(synchronizationManager, status, definition)
status
}
}
else -> {
// PROPAGATION_REQUIRED, PROPAGATION_SUPPORTS, PROPAGATION_MANDATORY:
// regular participation in existing transaction.
if (debugEnabled) {
logger.debug("Participating in existing transaction")
}
return Future.succeededFuture(this.prepareVertxTransaction(synchronizationManager, definition, transaction, false, debugEnabled, null))
}
}
}
private fun suspend(synchronizationManager: VertxTransactionSynchronizationManager, transaction: Any?): Future<SuspendedResourcesHolder?> {
if (synchronizationManager.isSynchronizationActive()) {
val suspendedSynchronizations = this.doSuspendSynchronization(synchronizationManager)
return suspendedSynchronizations.flatMap { synchronizations ->
val suspendedResources = if (transaction != null) this.doSuspend(synchronizationManager, transaction) else Future.succeededFuture()
return@flatMap suspendedResources.map {
val name = synchronizationManager.getCurrentTransactionName()
synchronizationManager.setCurrentTransactionName(null)
val readOnly = synchronizationManager.getCurrentTransactionReadOnly()
synchronizationManager.setCurrentTransactionReadOnly(false)
val isolationLevel = synchronizationManager.getCurrentTransactionIsolationLevel()
synchronizationManager.setCurrentTransactionIsolationLevel(null)
val wasActive = synchronizationManager.isActualTransactionActive()
synchronizationManager.setActualTransactionActive(false)
SuspendedResourcesHolder(it, synchronizations, name, readOnly, isolationLevel, wasActive)
}.recover {
if (ErrorPredicates.RUNTIME_OR_ERROR.test(it)) {
return@recover this.doResumeSynchronization(synchronizationManager, synchronizations).map { null }
}
return@recover Future.failedFuture(it)
}
}
} else if (transaction != null) {
// Transaction active but no synchronization active.
return this.doSuspend(synchronizationManager, transaction).map { SuspendedResourcesHolder(it) }
} else {
return Future.succeededFuture()
}
}
private fun doSuspendSynchronization(synchronizationManager: VertxTransactionSynchronizationManager): Future<List<VertxTransactionSynchronization>> {
val suspendedSynchronizations = synchronizationManager.getSynchronizations()
return Future.all(suspendedSynchronizations.map { it.suspend() }).compose { Future.succeededFuture(suspendedSynchronizations) }
}
private fun resume(synchronizationManager: VertxTransactionSynchronizationManager, transaction: Any?, resourcesHolder: SuspendedResourcesHolder?): Future<Void> {
var resume = Future.succeededFuture<Void>()
if (resourcesHolder != null) {
val suspendedResources = resourcesHolder.suspendedResources
if (suspendedResources != null) {
resume = this.doResume(synchronizationManager, transaction, suspendedResources)
}
val suspendedSynchronizations = resourcesHolder.suspendedSynchronizations
if (suspendedSynchronizations != null) {
synchronizationManager.setActualTransactionActive(resourcesHolder.wasActive)
synchronizationManager.setCurrentTransactionIsolationLevel(resourcesHolder.isolationLevel)
synchronizationManager.setCurrentTransactionReadOnly(resourcesHolder.readOnly)
synchronizationManager.setCurrentTransactionName(resourcesHolder.name)
return resume.flatMap { this.doResumeSynchronization(synchronizationManager, suspendedSynchronizations) }
}
}
return resume
}
private fun resumeAfterBeginException(
synchronizationManager: VertxTransactionSynchronizationManager,
transaction: Any?,
suspendedResources: SuspendedResourcesHolder?,
beginEx: Throwable
): Future<Void> {
val exMessage = "Inner transaction begin exception overridden by outer transaction resume exception"
return this.resume(synchronizationManager, transaction, suspendedResources).onFailure {
if (ErrorPredicates.RUNTIME_OR_ERROR.test(it)) {
logger.error(exMessage, beginEx)
}
}
}
private fun doResumeSynchronization(synchronizationManager: VertxTransactionSynchronizationManager, synchronizations: List<VertxTransactionSynchronization>): Future<Void> {
synchronizationManager.initSynchronization()
return Future.all(synchronizations.map { it.resume() }).flatMap {
synchronizations.forEach { synchronizationManager.registerSynchronization(it) }
Future.succeededFuture()
}
}
private fun processCommit(synchronizationManager: VertxTransactionSynchronizationManager, status: GenericVertxTransaction): Future<Void> {
val beforeCompletionInvoked = AtomicBoolean()
val commit = this.prepareForCommit(synchronizationManager, status)
.flatMap { this.triggerBeforeCommit(synchronizationManager, status) }
.flatMap { this.triggerBeforeCompletion(synchronizationManager, status) }
.flatMap {
beforeCompletionInvoked.set(true)
if (status.isNewTransaction) {
if (status.debug) {
logger.debug("Initiating transaction commit")
}
this.transactionExecutionListeners.forEach { it.beforeCommit(status) }
return@flatMap this.doCommit(synchronizationManager, status)
}
return@flatMap Future.succeededFuture()
}.recover { ex ->
val propagateException = Future.failedFuture<Void>(ex)
// Store result in a local variable in order to appease the
// Eclipse compiler with regard to inferred generics.
var result = propagateException
if (ErrorPredicates.UNEXPECTED_ROLLBACK.test(ex)) {
result = this.triggerAfterCompletion(synchronizationManager, status, VertxTransactionSynchronizationManager.STATUS_ROLLED_BACK).flatMap {
if (status.isNewTransaction) {
this.transactionExecutionListeners.forEach { it.afterRollback(status, null) }
}
return@flatMap propagateException
}
} else if (ErrorPredicates.TRANSACTION_EXCEPTION.test(ex)) {
result = this.triggerAfterCompletion(synchronizationManager, status, VertxTransactionSynchronizationManager.STATUS_UNKNOWN).flatMap {
if (status.isNewTransaction) {
this.transactionExecutionListeners.forEach { it.afterCommit(status, ex) }
}
return@flatMap propagateException
}
} else if (ErrorPredicates.RUNTIME_OR_ERROR.test(ex)) {
result = if (!beforeCompletionInvoked.get()) {
this.triggerBeforeCompletion(synchronizationManager, status)
} else {
Future.succeededFuture()
}.flatMap { this.doRollbackOnCommitException(synchronizationManager, status, ex) }.flatMap { propagateException }
}
return@recover result
}.flatMap {
return@flatMap this.triggerAfterCommit(synchronizationManager, status)
.recover { ex -> this.triggerAfterCompletion(synchronizationManager, status, VertxTransactionSynchronizationManager.STATUS_COMMITTED).flatMap { Future.failedFuture(ex) } }
.flatMap { this.triggerAfterCompletion(synchronizationManager, status, VertxTransactionSynchronizationManager.STATUS_COMMITTED) }
.flatMap {
if (status.isNewTransaction) {
this.transactionExecutionListeners.forEach { it.afterCommit(status, null) }
}
return@flatMap Future.succeededFuture<Void>()
}
}
return commit.flatMap { this.cleanupAfterCompletion(synchronizationManager, status) }
.recover { ex -> this.cleanupAfterCompletion(synchronizationManager, status).flatMap { Future.failedFuture<Void>(ex) } }
}
private fun triggerBeforeCommit(synchronizationManager: VertxTransactionSynchronizationManager, status: GenericVertxTransaction): Future<Void> {
if (status.isNewSynchronization()) {
return VertxTransactionSynchronizationUtils.triggerBeforeCommit(synchronizationManager.getSynchronizations(), status.isReadOnly)
}
return Future.succeededFuture()
}
private fun triggerBeforeCompletion(synchronizationManager: VertxTransactionSynchronizationManager, status: GenericVertxTransaction): Future<Void> {
if (status.isNewSynchronization()) {
return VertxTransactionSynchronizationUtils.triggerBeforeCompletion(synchronizationManager.getSynchronizations())
}
return Future.succeededFuture()
}
private fun triggerAfterCommit(synchronizationManager: VertxTransactionSynchronizationManager, status: GenericVertxTransaction): Future<Void> {
if (status.isNewSynchronization()) {
return VertxTransactionSynchronizationUtils.invokeAfterCommit(synchronizationManager.getSynchronizations())
}
return Future.succeededFuture()
}
private fun triggerAfterCompletion(synchronizationManager: VertxTransactionSynchronizationManager, status: GenericVertxTransaction, completionStatus: Int): Future<Void> {
if (status.isNewSynchronization()) {
val synchronizations = synchronizationManager.getSynchronizations()
synchronizationManager.clearSynchronization()
if (!status.hasTransaction() || status.isNewTransaction) {
// No transaction or new transaction for the current scope ->
// invoke the afterCompletion callbacks immediately
return this.invokeAfterCompletion(synchronizationManager, synchronizations, completionStatus)
} else if (!synchronizations.isEmpty()) {
// Existing transaction that we participate in, controlled outside
// the scope of this Spring transaction manager -> try to register
// an afterCompletion callback with the existing (JTA) transaction.
return this.registerAfterCompletionWithExistingTransaction(synchronizationManager, status.transaction!!, synchronizations)
}
}
return Future.succeededFuture()
}
private fun invokeAfterCompletion(synchronizationManager: VertxTransactionSynchronizationManager, synchronizations: List<VertxTransactionSynchronization>, completionStatus: Int): Future<Void> {
return VertxTransactionSynchronizationUtils.invokeAfterCompletion(synchronizations, completionStatus)
}
private fun cleanupAfterCompletion(synchronizationManager: VertxTransactionSynchronizationManager, status: GenericVertxTransaction): Future<Void> {
return Future.succeededFuture<Void>().flatMap {
status.setCompleted()
if (status.isNewSynchronization()) {
synchronizationManager.clear()
}
var cleanup = Future.succeededFuture<Void>()
if (status.isNewTransaction) {
cleanup = this.doCleanupAfterCompletion(synchronizationManager, status.transaction)
}
if (status.suspendedResources != null) {
if (status.debug) {
logger.debug("Resuming suspended transaction after completion of inner transaction")
}
val transaction = (if (status.hasTransaction()) status.transaction else null)
return@flatMap cleanup.flatMap { this.resume(synchronizationManager, transaction, status.suspendedResources as SuspendedResourcesHolder?) }
}
return@flatMap cleanup
}
}
private fun processRollback(synchronizationManager: VertxTransactionSynchronizationManager, status: GenericVertxTransaction): Future<Void> {
return this.triggerBeforeCompletion(synchronizationManager, status).flatMap {
if (status.isNewTransaction) {
if (status.debug) {
logger.debug("Initiating transaction rollback")
}
this.transactionExecutionListeners.forEach { it.beforeRollback(status) }
return@flatMap this.doRollback(synchronizationManager, status)
} else {
var beforeCompletion = Future.succeededFuture<Void>()
// Participating in larger transaction
if (status.hasTransaction()) {
if (status.debug) {
logger.debug("Participating transaction failed - marking existing transaction as rollback-only")
}
beforeCompletion = this.doSetRollbackOnly(synchronizationManager, status)
} else {
logger.debug("Should roll back transaction but cannot - no transaction available")
}
return@flatMap beforeCompletion
}
}.recover { ex ->
if (ErrorPredicates.RUNTIME_OR_ERROR.test(ex)) {
this.triggerAfterCompletion(synchronizationManager, status, VertxTransactionSynchronizationManager.STATUS_UNKNOWN).flatMap {
if (status.isNewTransaction) {
this.transactionExecutionListeners.forEach { it.afterRollback(status, ex) }
}
return@flatMap Future.succeededFuture<Void>()
}.flatMap { Future.failedFuture(ex) }
} else Future.failedFuture(ex)
}
.flatMap { this.triggerAfterCompletion(synchronizationManager, status, VertxTransactionSynchronizationManager.STATUS_ROLLED_BACK) }
.flatMap {
if (status.isNewTransaction) {
this.transactionExecutionListeners.forEach { it.afterRollback(status, null) }
}
return@flatMap Future.succeededFuture<Void>()
}
.recover { ex -> this.cleanupAfterCompletion(synchronizationManager, status).flatMap { Future.failedFuture(ex) } }
.flatMap { this.cleanupAfterCompletion(synchronizationManager, status) }
}
private fun doRollbackOnCommitException(synchronizationManager: VertxTransactionSynchronizationManager, status: GenericVertxTransaction, ex: Throwable): Future<Void> {
return Future.succeededFuture<Void>().flatMap {
if (status.isNewTransaction) {
if (status.debug) {
logger.debug("Initiating transaction rollback after commit exception", ex)
}
this.doRollback(synchronizationManager, status)
} else if (status.hasTransaction()) {
if (status.debug) {
logger.debug("Marking existing transaction as rollback-only after commit exception", ex)
}
this.doSetRollbackOnly(synchronizationManager, status)
}
Future.succeededFuture<Void>()
}.flatMap {
this.triggerAfterCompletion(synchronizationManager, status, VertxTransactionSynchronizationManager.STATUS_ROLLED_BACK).flatMap {
this.transactionExecutionListeners.forEach { it.afterRollback(status, null) }
Future.succeededFuture<Void>()
}
}.recover { rbex ->
if (ErrorPredicates.RUNTIME_OR_ERROR.test(rbex)) {
logger.error("Commit exception overridden by rollback exception", ex)
this.triggerAfterCompletion(synchronizationManager, status, VertxTransactionSynchronizationManager.STATUS_UNKNOWN).flatMap {
this.transactionExecutionListeners.forEach { it.afterRollback(status, rbex) }
Future.succeededFuture<Void>()
}.flatMap { Future.failedFuture(rbex) }
} else Future.failedFuture(rbex)
}
}
private fun prepareVertxTransaction(
synchronizationManager: VertxTransactionSynchronizationManager,
definition: TransactionDefinition,
transaction: Any?,
newTransaction: Boolean,
debug: Boolean,
suspendedResources: Any?
): GenericVertxTransaction {
val status = this.newVertxTransaction(synchronizationManager, definition, transaction, newTransaction, false, debug, suspendedResources)
this.prepareSynchronization(synchronizationManager, status, definition)
return status
}
private fun newVertxTransaction(
synchronizationManager: VertxTransactionSynchronizationManager,
definition: TransactionDefinition,
transaction: Any?,
newTransaction: Boolean,
nested: Boolean,
debug: Boolean,
suspendedResources: Any?
): GenericVertxTransaction {
return GenericVertxTransaction(definition.name, transaction, newTransaction, !synchronizationManager.isSynchronizationActive(), nested, definition.isReadOnly, debug, suspendedResources)
}
private fun prepareSynchronization(synchronizationManager: VertxTransactionSynchronizationManager, status: GenericVertxTransaction, definition: TransactionDefinition) {
if (status.isNewSynchronization()) {
synchronizationManager.setActualTransactionActive(status.hasTransaction())
synchronizationManager.setCurrentTransactionIsolationLevel(if (definition.isolationLevel != TransactionDefinition.ISOLATION_DEFAULT) definition.isolationLevel else null)
synchronizationManager.setCurrentTransactionReadOnly(definition.isReadOnly)
synchronizationManager.setCurrentTransactionName(definition.name)
synchronizationManager.initSynchronization()
}
}
/**
* @see org.springframework.transaction.reactive.AbstractReactiveTransactionManager.SuspendedResourcesHolder
*/
private class SuspendedResourcesHolder {
var suspendedResources: Any? = null
var suspendedSynchronizations: List<VertxTransactionSynchronization>? = null
var name: String? = null
var readOnly = false
var isolationLevel: Int? = null
var wasActive = false
constructor (suspendedResources: Any?) {
this.suspendedResources = suspendedResources
}
constructor (suspendedResources: Any?, suspendedSynchronizations: List<VertxTransactionSynchronization>?, name: String?, readOnly: Boolean, isolationLevel: Int?, wasActive: Boolean) {
this.suspendedResources = suspendedResources
this.suspendedSynchronizations = suspendedSynchronizations
this.name = name
this.readOnly = readOnly
this.isolationLevel = isolationLevel
this.wasActive = wasActive
}
}
private enum class ErrorPredicates : Predicate<Throwable?> {
/**
* Predicate matching [RuntimeException] or [Error].
*/
RUNTIME_OR_ERROR {
override fun test(throwable: Throwable?): Boolean {
return throwable is java.lang.RuntimeException || throwable is java.lang.Error
}
},
/**
* Predicate matching [TransactionException].
*/
TRANSACTION_EXCEPTION {
override fun test(throwable: Throwable?): Boolean {
return throwable is TransactionException
}
},
/**
* Predicate matching [UnexpectedRollbackException].
*/
UNEXPECTED_ROLLBACK {
override fun test(throwable: Throwable?): Boolean {
return throwable is UnexpectedRollbackException
}
};
abstract override fun test(throwable: Throwable?): Boolean
}
}
VertxJdbcPoolTransactionManager
真正的TransactionManager实现类。
实现了当前上下文,事务的创建、获取、挂起、唤醒、提交、回滚,并回调事务生命周期的各种回调钩子。
kotlin
package com.luomin.vertx.tx.manager
import com.luomin.vertx.tx.extension.GenericVertxTransactions.commitVertxTransaction
import com.luomin.vertx.tx.extension.GenericVertxTransactions.getSqlConnection
import com.luomin.vertx.tx.extension.GenericVertxTransactions.getVertxTransaction
import com.luomin.vertx.tx.extension.GenericVertxTransactions.rollbackVertxTransaction
import com.luomin.vertx.tx.extension.VertxJDBCPoolUtils
import com.luomin.vertx.tx.extension.VertxTransactionSynchronizationManagers.bindVertxConnectionHolder
import com.luomin.vertx.tx.extension.VertxTransactionSynchronizationManagers.getVertxConnectionHolder
import com.luomin.vertx.tx.extension.VertxTransactionSynchronizationManagers.unbindVertxConnectionHolder
import com.luomin.vertx.tx.holder.VertxConnectionHolder
import com.luomin.vertx.tx.transaction.GenericVertxTransaction
import com.luomin.vertx.tx.transaction.VertxTransactionObject
import io.vertx.core.Future
import io.vertx.jdbcclient.JDBCPool
import io.vertx.sqlclient.SqlConnection
import io.vertx.sqlclient.Transaction
import org.springframework.transaction.CannotCreateTransactionException
import org.springframework.transaction.TransactionDefinition
import java.time.Duration
/**
* <br>description<br/>
* @see org.springframework.r2dbc.connection.R2dbcTransactionManager
* @see org.springframework.jdbc.datasource.DataSourceTransactionManager
*
* <br>@author luomin<br/>
* <br>2025/3/28 15:01<br/>
*/
class VertxJdbcPoolTransactionManager : AbstractVertxTransactionManager {
var jdbcPool: JDBCPool? = null
var enforceReadOnly = false
constructor(jdbcPool: JDBCPool) {
this.jdbcPool = jdbcPool
}
override fun doGetTransaction(synchronizationManager: VertxTransactionSynchronizationManager): Any {
val txObject = VertxTransactionObject()
val conHolder = synchronizationManager.getVertxConnectionHolder(this.jdbcPool!!)
txObject.setConnectionHolder(conHolder, false)
return txObject
}
override fun isExistingTransaction(transaction: Any): Boolean {
val txObject = transaction as VertxTransactionObject
return txObject.isTransactionActive()
}
override fun doBegin(
synchronizationManager: VertxTransactionSynchronizationManager,
transaction: Any,
definition: TransactionDefinition
): Future<Void> {
val txObject = transaction as VertxTransactionObject
return if (!txObject.hasConnectionHolder() || txObject.getConnectionHolder()!!.isSynchronizedWithTransaction) {
this.jdbcPool!!.connection.onSuccess {
if (logger.isDebugEnabled) {
logger.debug("Acquired SqlConnection [$it] for Vertx transaction")
}
txObject.setConnectionHolder(VertxConnectionHolder(it), true)
}
} else {
txObject.getConnectionHolder()!!.isSynchronizedWithTransaction = true
Future.succeededFuture(txObject.getConnectionHolder()!!.getConnection())
}.flatMap { sqlConnection ->
this.doBegin(sqlConnection, definition)
.flatMap {
txObject.getConnectionHolder()!!.transaction = it
prepareTransactionalConnection(sqlConnection, definition)
}
.onSuccess {
txObject.getConnectionHolder()!!.transactionActive = true
val timeout = this.determineTimeout(definition)
if (!timeout.isNegative || !timeout.isZero) {
txObject.getConnectionHolder()!!.setTimeoutInMillis(timeout.toMillis())
}
// Bind the connection holder to the thread.
if (txObject.isNewConnectionHolder) {
synchronizationManager.bindVertxConnectionHolder(this.jdbcPool!!, txObject.getConnectionHolder()!!)
}
}.recover { ex ->
if (txObject.isNewConnectionHolder) {
// 释放连接回JDBCPool
return@recover VertxJDBCPoolUtils.releaseConnection(sqlConnection, this.jdbcPool!!).onComplete { txObject.setConnectionHolder(null, false) }.flatMap { Future.failedFuture(ex) }
}
return@recover Future.failedFuture(ex)
}
}.recover { Future.failedFuture(CannotCreateTransactionException("Could not open Vertx Connection for transaction", it)) }
}
override fun doSuspend(synchronizationManager: VertxTransactionSynchronizationManager, transaction: Any): Future<Any?> {
return Future.future<Any> {
val txObject = transaction as VertxTransactionObject
txObject.setConnectionHolder(null)
it.tryComplete(synchronizationManager.unbindVertxConnectionHolder(this.jdbcPool!!))
}
}
override fun doResume(synchronizationManager: VertxTransactionSynchronizationManager, transaction: Any?, suspendedResources: Any): Future<Void> {
return Future.future<Void> {
synchronizationManager.bindVertxConnectionHolder(this.jdbcPool!!, suspendedResources as VertxConnectionHolder)
it.tryComplete()
}
}
override fun doCommit(synchronizationManager: VertxTransactionSynchronizationManager, status: GenericVertxTransaction): Future<Void> {
val sqlConnection = status.getSqlConnection()
if (status.debug) {
logger.debug("Committing Vertx transaction on SqlConnection [$sqlConnection]")
}
return status.commitVertxTransaction()
}
override fun doRollback(synchronizationManager: VertxTransactionSynchronizationManager, status: GenericVertxTransaction): Future<Void> {
val sqlConnection = status.getSqlConnection()
if (status.debug) {
logger.debug("Rolling back Vertx transaction on SqlConnection [$sqlConnection]")
}
return status.rollbackVertxTransaction()
}
override fun doSetRollbackOnly(synchronizationManager: VertxTransactionSynchronizationManager, status: GenericVertxTransaction): Future<Void> {
return Future.future {
val txObject = status.getVertxTransaction()
val sqlConnection = status.getSqlConnection()
if (status.debug) {
logger.debug("Setting Vertx transaction on SqlConnection [$sqlConnection] rollback-only")
}
txObject.setRollbackOnly()
it.tryComplete()
}
}
override fun doCleanupAfterCompletion(synchronizationManager: VertxTransactionSynchronizationManager, transaction: Any?): Future<Void> {
return Future.succeededFuture<Void>().flatMap {
val txObject = transaction as VertxTransactionObject
if (txObject.isNewConnectionHolder) {
synchronizationManager.unbindVertxConnectionHolder(this.jdbcPool!!)
}
// 释放连接
try {
if (txObject.isNewConnectionHolder) {
val sqlConnection = txObject.getConnectionHolder()!!.getConnection()
if (logger.isDebugEnabled) {
logger.debug("Releasing Vertx SqlConnection [$sqlConnection] after transaction")
}
return@flatMap VertxJDBCPoolUtils.releaseConnection(sqlConnection, this.jdbcPool!!)
}
} finally {
txObject.getConnectionHolder()!!.clear()
}
Future.succeededFuture()
}
}
private fun doBegin(sqlConnection: SqlConnection, definition: Any): Future<Transaction> {
if (logger.isDebugEnabled) {
logger.debug("Starting Vertx transaction on SqlConnection [$sqlConnection] using [$definition]")
}
return sqlConnection.begin()
}
fun prepareTransactionalConnection(sqlConnection: SqlConnection, definition: TransactionDefinition): Future<Void> {
if (this.enforceReadOnly && definition.isReadOnly) {
return sqlConnection.preparedQuery("SET TRANSACTION READ ONLY").execute().mapEmpty<Void>()
}
return Future.succeededFuture()
}
private fun determineTimeout(definition: TransactionDefinition): Duration {
if (definition.timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
return Duration.ofSeconds(definition.timeout.toLong())
}
return Duration.ZERO
}
}
VertxTransactionSynchronizationManager 事务同步器管理器
持有事务上下文 VertxTransactionContext、能组合调用 VertxTransactionSynchronization。
kotlin
package com.luomin.vertx.tx.manager
import com.luomin.vertx.extension.core.Futures.recover
import com.luomin.vertx.tx.transaction.VertxTransactionContext
import com.luomin.vertx.tx.transaction.VertxTransactionSynchronization
import com.luomin.vertx.tx.util.VertxTransactionSynchronizationUtils
import io.vertx.core.Future
import io.vertx.core.Vertx
import org.springframework.core.annotation.AnnotationAwareOrderComparator
import org.springframework.transaction.NoTransactionException
import org.springframework.util.Assert
import java.util.*
/**
* <br>description<br/>
* @see org.springframework.transaction.reactive.TransactionSynchronizationManager
*
* <br>@author luomin<br/>
* <br>2025/3/31 15:09<br/>
*/
class VertxTransactionSynchronizationManager(val transactionContext: VertxTransactionContext) {
fun hasResource(key: Any): Boolean {
val actualKey = VertxTransactionSynchronizationUtils.unwrapResourceIfNecessary(key)
val value = doGetResource(actualKey)
return (value != null)
}
fun getResource(key: Any): Any? {
val actualKey = VertxTransactionSynchronizationUtils.unwrapResourceIfNecessary(key)
return doGetResource(actualKey)
}
fun bindResource(key: Any, value: Any) {
val actualKey = VertxTransactionSynchronizationUtils.unwrapResourceIfNecessary(key)
Assert.notNull(value, "Value must not be null")
val map = this.transactionContext.resources
val oldValue = map.put(actualKey, value)
check(oldValue == null) { "Already value [$oldValue] for key [$actualKey] bound to context" }
}
fun unbindResource(key: Any): Any {
val actualKey = VertxTransactionSynchronizationUtils.unwrapResourceIfNecessary(key)
val value = this.unbindResourceIfPossible(actualKey)
checkNotNull(value) { "No value for key [$actualKey] bound to context" }
return value
}
fun unbindResourceIfPossible(key: Any): Any? {
val actualKey = VertxTransactionSynchronizationUtils.unwrapResourceIfNecessary(key)
return doUnbindResource(actualKey)
}
fun setActualTransactionActive(active: Boolean) {
this.transactionContext.actualTransactionActive = active
}
fun isActualTransactionActive(): Boolean {
return this.transactionContext.actualTransactionActive
}
fun setCurrentTransactionReadOnly(readOnly: Boolean) {
this.transactionContext.currentTransactionReadOnly = readOnly
}
fun getCurrentTransactionReadOnly(): Boolean {
return this.transactionContext.currentTransactionReadOnly
}
fun setCurrentTransactionName(name: String?) {
this.transactionContext.currentTransactionName = name
}
fun getCurrentTransactionName(): String? {
return this.transactionContext.currentTransactionName
}
fun setCurrentTransactionIsolationLevel(isolationLevel: Int?) {
this.transactionContext.currentTransactionIsolationLevel = isolationLevel
}
fun getCurrentTransactionIsolationLevel(): Int? {
return this.transactionContext.currentTransactionIsolationLevel
}
fun isSynchronizationActive(): Boolean {
return this.transactionContext.synchronizations != null
}
fun initSynchronization() {
check(!isSynchronizationActive()) { "Cannot activate transaction synchronization - already active" }
this.transactionContext.synchronizations = LinkedHashSet<VertxTransactionSynchronization>()
}
fun registerSynchronization(synchronization: VertxTransactionSynchronization) {
Assert.notNull(synchronization, "TransactionSynchronization must not be null")
val synchs = this.transactionContext.synchronizations
checkNotNull(synchs) { "Transaction synchronization is not active" }
synchs.add(synchronization)
}
fun getSynchronizations(): List<VertxTransactionSynchronization> {
val synchs = this.transactionContext.synchronizations
checkNotNull(synchs) { "Transaction synchronization is not active" }
if (synchs.isEmpty()) {
return emptyList()
} else {
val sortedSynchs = ArrayList(synchs)
AnnotationAwareOrderComparator.sort(sortedSynchs)
return Collections.unmodifiableList(sortedSynchs)
}
}
fun clearSynchronization() {
check(isSynchronizationActive()) { "Cannot deactivate transaction synchronization - not active" }
this.transactionContext.synchronizations = null
}
fun clear() {
this.transactionContext.clear()
}
private fun doGetResource(actualKey: Any): Any? {
return this.transactionContext.resources[actualKey]
}
private fun doUnbindResource(actualKey: Any?): Any? {
val map = this.transactionContext.resources
return map.remove(actualKey)
}
companion object {
/** Completion status in case of proper commit. */
const val STATUS_COMMITTED: Int = 0
/** Completion status in case of proper rollback. */
const val STATUS_ROLLED_BACK: Int = 1
/** Completion status in case of heuristic mixed completion or system errors. */
const val STATUS_UNKNOWN: Int = 2
fun currentTransaction(): VertxTransactionSynchronizationManager {
return VertxTransactionSynchronizationManager(VertxTransactionContextManager.currentContext())
}
fun currentTransaction(vertx: Vertx): VertxTransactionSynchronizationManager {
return VertxTransactionSynchronizationManager(VertxTransactionContextManager.currentContext(vertx))
}
fun forCurrentTransaction(): Future<VertxTransactionSynchronizationManager> {
return VertxTransactionContextManager.forCurrentContext().map { VertxTransactionSynchronizationManager(it) }
}
fun forCurrentTransaction(vertx: Vertx): Future<VertxTransactionSynchronizationManager> {
return return VertxTransactionContextManager.forCurrentContext(vertx).map { VertxTransactionSynchronizationManager(it) }
}
fun forCurrentTransactionNullable(): Future<VertxTransactionSynchronizationManager?> {
return VertxTransactionContextManager.forCurrentContext().map { VertxTransactionSynchronizationManager(it) }
.recover(NoTransactionException::class.java) { Future.succeededFuture<VertxTransactionSynchronizationManager?>(null) }
}
fun forCurrentTransactionNullable(vertx: Vertx): Future<VertxTransactionSynchronizationManager?> {
return return VertxTransactionContextManager.forCurrentContext(vertx).map { VertxTransactionSynchronizationManager(it) }
.recover(NoTransactionException::class.java) { Future.succeededFuture<VertxTransactionSynchronizationManager?>(null) }
}
}
}
基础--事务对象

VertxTransaction
实现Spring TransactionExecution 接口。
TransactionExecution 表示了事务执行体的状态。
kotlin
package com.luomin.vertx.tx.transaction
import org.springframework.transaction.TransactionExecution
/**
* <br>description<br/>
*
* <br>@author luomin<br/>
* <br>2025/3/31 14:31<br/>
*/
interface VertxTransaction : TransactionExecution
GenericVertxTransaction
VertxTransaction 的实现类,保存了事务执行过程的各种状态标识。
kotlin
package com.luomin.vertx.tx.transaction
/**
* <br>description<br/>
* @see com.luomin.vertx.tx.transaction.GenericVertxTransaction
*
* <br>@author luomin<br/>
* <br>2025/3/31 16:10<br/>
*/
class GenericVertxTransaction: VertxTransaction {
private val transactionName: String?
val transaction: Any?
val newTransaction: Boolean
private val newSynchronization: Boolean
val nested: Boolean
var readOnly: Boolean
val debug: Boolean
val suspendedResources: Any?
constructor(transactionName: String?, transaction: Any?, newTransaction: Boolean, newSynchronization: Boolean, nested: Boolean, readOnly: Boolean, debug: Boolean, suspendedResources: Any?) {
this.transactionName = transactionName
this.transaction = transaction
this.newTransaction = newTransaction
this.newSynchronization = newSynchronization
this.nested = nested
this.readOnly = readOnly
this.debug = debug
this.suspendedResources = suspendedResources
}
private var completed = false
override fun getTransactionName(): String {
return this.transactionName!!
}
override fun hasTransaction(): Boolean {
return this.transaction != null
}
override fun isNewTransaction(): Boolean {
return this.hasTransaction() && this.newTransaction
}
override fun isNested(): Boolean {
return this.nested
}
override fun isReadOnly(): Boolean {
return this.readOnly
}
override fun setRollbackOnly() {
check(!this.completed) { "Transaction completed" }
this.readOnly = true
}
override fun isRollbackOnly(): Boolean {
return this.readOnly
}
override fun isCompleted(): Boolean {
return this.completed
}
fun setCompleted() {
this.completed = true
}
fun isNewSynchronization(): Boolean {
return this.newSynchronization
}
}
VertxTransactionContext 事务上下文
封装了事务的资源和同步器,通常由外部 VertxTransactionContextHolder 持有。
VertxTransactionContextHolder 由Vertx上下文维护。
kotlin
package com.luomin.vertx.tx.transaction
/**
* <br>description<br/>
* @see org.springframework.transaction.reactive.TransactionContext
*
* <br>@author luomin<br/>
* <br>2025/3/28 15:10<br/>
*/
class VertxTransactionContext {
val resources = LinkedHashMap<Any, Any?>()
var synchronizations: LinkedHashSet<VertxTransactionSynchronization>? = null
@Volatile
var currentTransactionName: String? = null
@Volatile
var currentTransactionReadOnly = false
@Volatile
var currentTransactionIsolationLevel: Int? = null
@Volatile
var actualTransactionActive = false
fun clear() {
this.synchronizations = null
this.currentTransactionName = null
this.currentTransactionReadOnly = false
this.currentTransactionIsolationLevel = null
this.actualTransactionActive = false
}
}
VertxTransactionInfo
持有当前事务对象的一些信息。
如:使用到的事务管理器VertxTransactionManager、事务配置属性TransactionAttribute、事务的唯一切点标识 joinPointIdentification
kotlin
package com.luomin.vertx.tx.transaction
import com.luomin.vertx.tx.manager.VertxTransactionManager
import org.springframework.transaction.interceptor.TransactionAttribute
/**
* <br>description<br/>
*
* <br>@author luomin<br/>
* <br>2025/3/28 16:24<br/>
*/
class VertxTransactionInfo(val transactionManager: VertxTransactionManager, val transactionAttribute: TransactionAttribute?, val joinPointIdentification: String) {
var vertxTransaction: VertxTransaction? = null
fun newVertxTransaction(transaction: VertxTransaction?) {
this.vertxTransaction = transaction
}
}
VertxTransactionObject
持有ConnectionHolder,作为事务管理器运行期间的参数传递,如DTO一样。
kotlin
package com.luomin.vertx.tx.transaction
import com.luomin.vertx.tx.holder.VertxConnectionHolder
/**
* description <br/>
*
* @author luomin <br/>
* 2025/3/17 14:54 <br/>
*/
class VertxTransactionObject {
private var connectionHolder: VertxConnectionHolder? = null
var isNewConnectionHolder = false
constructor()
fun setConnectionHolder(connectionHolder: VertxConnectionHolder?, newConnectionHolder: Boolean) {
this.connectionHolder = connectionHolder
this.isNewConnectionHolder = newConnectionHolder
}
fun setConnectionHolder(connectionHolder: VertxConnectionHolder?) {
this.connectionHolder = connectionHolder
}
fun getConnectionHolder(): VertxConnectionHolder? {
return connectionHolder
}
fun hasConnectionHolder(): Boolean {
return connectionHolder != null
}
fun setRollbackOnly() {
this.getConnectionHolder()!!.setRollbackOnly()
}
fun isTransactionActive(): Boolean {
return this.connectionHolder != null && this.connectionHolder!!.transactionActive
}
}
VertxTransactionSynchronization 事务同步器
负责将Spring事务的生命周期同步到其他事务的生命周期。
kotlin
package com.luomin.vertx.tx.transaction
import io.vertx.core.Future
/**
* <br>description<br/>
* @see org.springframework.transaction.support.TransactionSynchronization
* @see org.springframework.transaction.reactive.TransactionSynchronization
*
* <br>@author luomin<br/>
* <br>2025/4/1 10:10<br/>
*/
@JvmDefaultWithoutCompatibility
interface VertxTransactionSynchronization {
fun suspend(): Future<Void> {
return Future.succeededFuture()
}
fun resume(): Future<Void> {
return Future.succeededFuture()
}
fun beforeCommit(readOnly: Boolean): Future<Void> {
return Future.succeededFuture()
}
fun beforeCompletion(): Future<Void> {
return Future.succeededFuture()
}
fun afterCommit(): Future<Void> {
return Future.succeededFuture()
}
fun afterCompletion(status: Int): Future<Void> {
return Future.succeededFuture()
}
}
VertxConnectionTransactionSynchronization 数据库VertxConnection同步器
负责将Spring的事务生命周期,同步到 vertx-jdbc-client 的事务生命周期。
kotlin
package com.luomin.vertx.tx.transaction
import com.luomin.vertx.tx.extension.VertxJDBCPoolUtils
import com.luomin.vertx.tx.extension.VertxTransactionSynchronizationManagers.bindVertxConnectionHolder
import com.luomin.vertx.tx.extension.VertxTransactionSynchronizationManagers.unbindVertxConnectionHolder
import com.luomin.vertx.tx.extension.VertxTransactionSynchronizationManagers.unbindVertxConnectionHolderIfPossible
import com.luomin.vertx.tx.holder.VertxConnectionHolder
import com.luomin.vertx.tx.manager.VertxTransactionSynchronizationManager
import io.vertx.core.Future
import io.vertx.jdbcclient.JDBCPool
import org.springframework.core.Ordered
/**
* 负责同步数据库事务与spring事务 <br/>
* @see org.springframework.r2dbc.connection.ConnectionFactoryUtils.ConnectionSynchronization
* @see org.springframework.jdbc.datasource.DataSourceUtils.ConnectionSynchronization
*
* @author luomin <br/>
* 2025/3/19 11:44 <br/>
*/
class VertxConnectionTransactionSynchronization(val conHolder: VertxConnectionHolder, val jdbcPool: JDBCPool) : VertxTransactionSynchronization, Ordered {
private var holderActive = true
override fun getOrder(): Int {
return VertxJDBCPoolUtils.CONNECTION_SYNCHRONIZATION_ORDER - 1
}
override fun suspend(): Future<Void> {
if (holderActive) {
return VertxTransactionSynchronizationManager.forCurrentTransaction().flatMap { synchronizationManager ->
synchronizationManager.unbindVertxConnectionHolder(this.jdbcPool)
if (this.conHolder.hasConnection() && !this.conHolder.isOpen) {
return@flatMap VertxJDBCPoolUtils.releaseConnection(this.conHolder.getConnection(), this.jdbcPool).onComplete { this.conHolder.setConnection(null) }
}
return@flatMap Future.succeededFuture()
}
}
return super.suspend()
}
override fun resume(): Future<Void> {
if (holderActive) {
return VertxTransactionSynchronizationManager.forCurrentTransaction().flatMap {
it.bindVertxConnectionHolder(this.jdbcPool, this.conHolder)
Future.succeededFuture()
}
}
return super.suspend()
}
override fun beforeCompletion(): Future<Void> {
if (!this.conHolder.isOpen) {
return VertxTransactionSynchronizationManager.forCurrentTransaction().flatMap { synchronizationManager ->
synchronizationManager.unbindVertxConnectionHolder(this.jdbcPool)
this.holderActive = false
if (this.conHolder.hasConnection()) {
return@flatMap VertxJDBCPoolUtils.releaseConnection(this.conHolder.getConnection(), this.jdbcPool)
}
return@flatMap Future.succeededFuture()
}
}
return super.beforeCompletion()
}
override fun afterCompletion(status: Int): Future<Void> {
if (holderActive) {
return VertxTransactionSynchronizationManager.forCurrentTransaction().flatMap { synchronizationManager ->
synchronizationManager.unbindVertxConnectionHolderIfPossible(this.jdbcPool)
this.holderActive = false
if (this.conHolder.hasConnection()) {
return@flatMap VertxJDBCPoolUtils.releaseConnection(this.conHolder.getConnection(), this.jdbcPool).onComplete { this.conHolder.setConnection(null) }
}
return@flatMap Future.succeededFuture()
}
}
this.conHolder.reset()
return super.afterCompletion(status)
}
}
核心工具类
VertxJDBCPoolUtils
封装了从Vertx JDBCPool 获取/释放 SqlConnection 的能力。
在这其中,利用 VertxTransactionSynchronizationManager 判断当前上下文的事务是否需要 绑定/释放 SqlConnection 资源。
kotlin
package com.luomin.vertx.tx.extension
import com.luomin.vertx.extension.core.Futures.recover
import com.luomin.vertx.tx.extension.VertxTransactionSynchronizationManagers.bindVertxConnectionHolder
import com.luomin.vertx.tx.extension.VertxTransactionSynchronizationManagers.getVertxConnectionHolder
import com.luomin.vertx.tx.holder.VertxConnectionHolder
import com.luomin.vertx.tx.manager.VertxTransactionSynchronizationManager
import com.luomin.vertx.tx.transaction.VertxConnectionTransactionSynchronization
import io.vertx.core.Future
import io.vertx.jdbcclient.JDBCPool
import io.vertx.sqlclient.SqlConnection
import org.apache.commons.logging.LogFactory
import org.springframework.dao.DataAccessResourceFailureException
import org.springframework.transaction.NoTransactionException
/**
* description <br/>
* @see org.springframework.r2dbc.connection.ConnectionFactoryUtils
* @see org.springframework.jdbc.datasource.DataSourceUtils
*
* @author luomin <br/>
* 2025/3/18 11:41 <br/>
*/
object VertxJDBCPoolUtils {
private val logger = LogFactory.getLog(VertxJDBCPoolUtils::class.java)
const val CONNECTION_SYNCHRONIZATION_ORDER: Int = 1000
fun isConnectionTransactional(sqlConnection: SqlConnection, jdbcPool: JDBCPool): Future<Boolean> {
return VertxTransactionSynchronizationManager.forCurrentTransaction().flatMap { synchronizationManager ->
val conHolder = synchronizationManager.getVertxConnectionHolder(jdbcPool)
Future.succeededFuture(conHolder != null && this.sqlConnectionEquals(conHolder, sqlConnection))
}.recover(NoTransactionException::class.java) { Future.succeededFuture(false) }
}
fun getConnection(jdbCPool: JDBCPool): Future<SqlConnection> {
return this.doGetConnection(jdbCPool).recover { Future.failedFuture(DataAccessResourceFailureException("Failed to obtain Vertx SqlConnection", it)) }
}
fun releaseConnection(sqlConnection: SqlConnection, jdbcPool: JDBCPool): Future<Void> {
return this.doReleaseConnection(sqlConnection, jdbcPool).recover { Future.failedFuture(DataAccessResourceFailureException("Failed to close Vertx SqlConnection", it)) }
}
fun doGetConnection(jdbcPool: JDBCPool): Future<SqlConnection> {
return VertxTransactionSynchronizationManager.forCurrentTransaction().flatMap { synchronizationManager ->
val conHolder = synchronizationManager.getVertxConnectionHolder(jdbcPool)
if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction)) {
conHolder.requested()
if (!conHolder.hasConnection()) {
return@flatMap this.fetchConnection(jdbcPool).onSuccess { conHolder.setConnection(it) }.onSuccess { logger.debug("Fetching resumed Vertx SqlConnection [$it] from JDBCPool") }
}
return@flatMap Future.succeededFuture(conHolder.getConnection())
}
// Else we either got no holder or an empty thread-bound holder here.
val connFuture = this.fetchConnection(jdbcPool).onSuccess { logger.debug("Fetching Vertx SqlConnection [$it] from JDBCPool") }
if (synchronizationManager.isSynchronizationActive()) {
return@flatMap connFuture.map { connection ->
// Use same Connection for further R2DBC actions within the transaction.
// Thread-bound object will get removed by synchronization at transaction completion.
var holderToUse = conHolder
if (holderToUse == null) {
holderToUse = VertxConnectionHolder(connection)
} else {
holderToUse.setConnection(connection)
}
holderToUse.requested()
synchronizationManager.registerSynchronization(VertxConnectionTransactionSynchronization(holderToUse, jdbcPool))
holderToUse.isSynchronizedWithTransaction = true
if (holderToUse != conHolder) {
synchronizationManager.bindVertxConnectionHolder(jdbcPool, holderToUse)
}
connection
}
}
return@flatMap connFuture
}.recover(NoTransactionException::class.java) { this.fetchConnection(jdbcPool) }
}
fun doReleaseConnection(sqlConnection: SqlConnection, jdbcPool: JDBCPool): Future<Void> {
return VertxTransactionSynchronizationManager.forCurrentTransaction().flatMap { synchronizationManager ->
val conHolder = synchronizationManager.getVertxConnectionHolder(jdbcPool)
if (conHolder != null && this.sqlConnectionEquals(conHolder, sqlConnection)) {
// It's the transactional Connection: Don't close it.
logger.debug("released Vertx SqlConnection [$sqlConnection]")
conHolder.released()
return@flatMap Future.succeededFuture()
}
logger.debug("close Vertx SqlConnection [$sqlConnection]")
sqlConnection.close()
}.recover(NoTransactionException::class.java) { sqlConnection.close() }
}
private fun sqlConnectionEquals(conHolder: VertxConnectionHolder, sqlConnection: SqlConnection): Boolean {
if (!conHolder.hasConnection()) return false
return conHolder.getConnection() == sqlConnection
}
private fun fetchConnection(jdbcPool: JDBCPool): Future<SqlConnection> {
return jdbcPool.connection
}
}
核心Holder对象
VertxConnectionHolder
实现了Spring ResourceHolderSupport 接口,表明这是一个事务的资源。
这里,我们持有的资源,是Vertx的SqlConnection
kotlin
package com.luomin.vertx.tx.holder
import io.vertx.sqlclient.SqlConnection
import io.vertx.sqlclient.Transaction
import org.springframework.transaction.support.ResourceHolderSupport
/**
* description <br/>
*
* @author luomin <br/>
* 2025/3/17 15:24 <br/>
*/
class VertxConnectionHolder : ResourceHolderSupport {
private var currentConnection: SqlConnection? = null
var transactionActive = false
var transaction: Transaction? = null
constructor(currentConnection: SqlConnection?) : super() {
this.currentConnection = currentConnection
}
fun getConnection(): SqlConnection {
return currentConnection!!
}
fun setConnection(connection: SqlConnection?) {
this.currentConnection = connection
}
fun hasConnection(): Boolean {
return currentConnection != null
}
}
VertxTransactionContextHolder
持有 VertxTransactionContext,这里使用的是Deque,主要是为了嵌套事务的情况。栈顶的就是当前事务。
kotlin
package com.luomin.vertx.tx.holder
import com.luomin.vertx.tx.transaction.VertxTransactionContext
import org.springframework.transaction.NoTransactionException
import java.util.*
/**
* <br>description<br/>
* @see org.springframework.transaction.reactive.TransactionContextHolder
*
* <br>@author luomin<br/>
* <br>2025/3/28 15:10<br/>
*/
class VertxTransactionContextHolder(val transactionStack: ArrayDeque<VertxTransactionContext>) {
fun hasContext(): Boolean {
return this.transactionStack.isNotEmpty()
}
fun currentContext(): VertxTransactionContext {
return this.transactionStack.peek() ?: throw NoTransactionException("No transaction in context")
}
fun createContext(): VertxTransactionContext {
var context = this.transactionStack.peek()
if (context == null) {
context = VertxTransactionContext()
this.transactionStack.push(context)
}
return context
}
}
TransactionManagerHolder 兼容性 事务管理器持有器
实现了PlatformTransactionManager、ReactiveTransactionManager、VertxTransactionManager等接口,并持有相应事务管理器Bean,以实现Spring支持多种事务管理器。
kotlin
package com.luomin.vertx.tx.holder
import com.luomin.vertx.tx.manager.VertxTransactionManager
import com.luomin.vertx.tx.transaction.VertxTransaction
import io.vertx.core.Future
import org.springframework.transaction.*
import reactor.core.publisher.Mono
/**
* <br>description<br/>
*
* @author luomin
* <br>2025/4/22 16:53<br/>
*/
class TransactionManagerHolder(val ptm: PlatformTransactionManager?, val rtm: ReactiveTransactionManager?, val vtm: VertxTransactionManager) : PlatformTransactionManager, ReactiveTransactionManager,
VertxTransactionManager {
override fun getTransaction(definition: TransactionDefinition?): TransactionStatus {
return ptm?.getTransaction(definition) ?: throw NotImplementedError()
}
override fun commit(status: TransactionStatus) {
return ptm?.commit(status) ?: throw NotImplementedError()
}
override fun rollback(status: TransactionStatus) {
return ptm?.rollback(status) ?: throw NotImplementedError()
}
override fun getReactiveTransaction(definition: TransactionDefinition?): Mono<ReactiveTransaction?> {
return rtm?.getReactiveTransaction(definition) ?: throw NotImplementedError()
}
override fun commit(transaction: ReactiveTransaction): Mono<Void?> {
return rtm?.commit(transaction) ?: throw NotImplementedError()
}
override fun rollback(transaction: ReactiveTransaction): Mono<Void?> {
return rtm?.rollback(transaction) ?: throw NotImplementedError()
}
override fun getVertxTransaction(definition: TransactionDefinition?): Future<VertxTransaction> {
return vtm.getVertxTransaction(definition)
}
override fun commit(transaction: VertxTransaction): Future<Void> {
return vtm.commit(transaction)
}
override fun rollback(transaction: VertxTransaction): Future<Void> {
return vtm.rollback(transaction)
}
}
核心--事务拦截器--入口
VertxTransactionInterceptor
继承了Spring TransactionInterceptor,Spring事务机制,会让这个拦截器接手事务处理。
我们这里直接使用一个委派模式,把Spring原生的TransactionInterceptor包裹起来。
拦截流程
- 探测出事务方法的事务配置 TransactionAttribute
- 探测出事务方法应该使用的是哪zhTransactionManager
- 构建 VertxTransactionSupport 包裹事务方法(适用于Vertx Future的事务)
- 执行 VertxTransactionSupport 对事务方法的事务环绕增强
事务执行流程
- 生成事务方法的唯一标识
- 构建Vertx上下文,让代码运行在同一个Vertx上下文中
- 构建事务上下文,VertxTransactionContext、VertxTransactionContextHolder
- 创建事务对象或者获取事务对象
- 执行目标方法
- 事务提交/事务回滚
这其中,会调用 VertxTransactionManager 的API。
kotlin
package com.luomin.vertx.tx.interceptor
import com.luomin.vertx.extension.core.Vertxs.futureOnContext
import com.luomin.vertx.tx.holder.TransactionManagerHolder
import com.luomin.vertx.tx.manager.VertxTransactionContextManager
import com.luomin.vertx.tx.manager.VertxTransactionManager
import com.luomin.vertx.tx.transaction.VertxTransaction
import com.luomin.vertx.tx.transaction.VertxTransactionInfo
import io.vertx.core.Future
import io.vertx.core.Promise
import io.vertx.core.Vertx
import org.apache.commons.logging.LogFactory
import org.springframework.transaction.TransactionManager
import org.springframework.transaction.TransactionSystemException
import org.springframework.transaction.interceptor.DefaultTransactionAttribute
import org.springframework.transaction.interceptor.DelegatingTransactionAttribute
import org.springframework.transaction.interceptor.TransactionAttribute
import org.springframework.transaction.interceptor.TransactionInterceptor
import org.springframework.util.ClassUtils
import org.springframework.util.ConcurrentReferenceHashMap
import reactor.core.publisher.Flux
import reactor.core.publisher.Mono
import java.lang.reflect.Method
/**
* <br>description<br/>
*
* <br>@author luomin<br/>
* <br>2025/3/28 14:54<br/>
*/
class VertxTransactionInterceptor(val vertx: Vertx) : TransactionInterceptor() {
init {
VertxTransactionContextManager.defaultVertx = vertx
}
override fun invokeWithinTransaction(method: Method, targetClass: Class<*>?, invocation: InvocationCallback): Any? {
// If the transaction attribute is null, the method is non-transactional.
val tas = transactionAttributeSource
var txAttr = tas?.getTransactionAttribute(method, targetClass)
txAttr = delegatingTransactionAttribute(txAttr, method, targetClass)
var tm = this.determineTransactionManager(txAttr, targetClass)
tm = determineTransactionManager(tm ?: super.determineTransactionManager(txAttr, targetClass), txAttr, method, targetClass)
if (tm == null) {
return super.invokeWithinTransaction(method, targetClass, invocation)
}
if (tm !is VertxTransactionManager) {
return super.invokeWithinTransaction(method, targetClass, invocation)
}
val txSupport = transactionSupportCache.computeIfAbsent(method) { VertxTransactionSupport(this.vertx) }
return txSupport.invokeWithinTransaction(method, targetClass, { invocation.proceedWithInvocation() as Future<Any?> }, txAttr, tm)
}
override fun determineTransactionManager(txAttr: TransactionAttribute?, targetClass: Class<*>?): TransactionManager? {
val tm = super.determineTransactionManager(txAttr, targetClass)
if (tm is TransactionManagerHolder) {
return VertxTransactionInterceptor.determineTransactionManager(txAttr)
}
return tm
}
companion object {
private val transactionNameManagerCache = ConcurrentReferenceHashMap<String, TransactionManager>(1024)
private val transactionSupportCache = ConcurrentReferenceHashMap<Method, VertxTransactionSupport>(1024)
fun determineTransactionManager(tx: TransactionManager?, txAttr: TransactionAttribute?, method: Method, targetClass: Class<*>?): TransactionManager? {
if (txAttr == null) return tx
val txName = txAttr.name
val isVertx = Future::class.java.isAssignableFrom(method.returnType)
// 2025/4/23 by luomin. reactiveAdapter的情况暂未考虑
val isReactive = Flux::class.java.isAssignableFrom(method.returnType) || Mono::class.java.isAssignableFrom(method.returnType)
if (isVertx) {
if (tx != null && tx is TransactionManagerHolder) {
return transactionNameManagerCache.computeIfAbsent(txName!!) { tx.vtm }
}
}
if (isReactive) {
if (tx != null && tx is TransactionManagerHolder) {
return transactionNameManagerCache.computeIfAbsent(txName!!) { tx.rtm }
}
}
if (tx != null && tx is TransactionManagerHolder) {
return transactionNameManagerCache.computeIfAbsent(txName!!) { tx.ptm }
}
return tx
}
fun determineTransactionManager(txAttr: TransactionAttribute?): TransactionManager? {
if (txAttr == null) {
return null
}
val txName = txAttr.name
if (txName.isNullOrBlank()) {
return null
}
return transactionNameManagerCache[txName]
}
fun delegatingTransactionAttribute(txAttr: TransactionAttribute?, method: Method, targetClass: Class<*>?): TransactionAttribute? {
return this.delegatingTransactionAttribute(txAttr, this.methodIdentification(method, targetClass, txAttr))
}
fun delegatingTransactionAttribute(txAttr: TransactionAttribute?, joinPointIdentification: String): TransactionAttribute? {
var useTxAttr = txAttr
if (useTxAttr != null && useTxAttr.name == null) {
useTxAttr = object : DelegatingTransactionAttribute(useTxAttr!!) {
override fun getName(): String {
return joinPointIdentification
}
}
}
return useTxAttr
}
fun methodIdentification(method: Method, targetClass: Class<*>?, txAttr: TransactionAttribute?): String {
var methodIdentification: String? = null
if (txAttr is DefaultTransactionAttribute) {
methodIdentification = txAttr.descriptor
}
if (methodIdentification == null) {
methodIdentification = ClassUtils.getQualifiedMethodName(method, targetClass)
}
return methodIdentification
}
}
/**
* @see org.springframework.transaction.interceptor.TransactionAspectSupport.ReactiveTransactionSupport
*/
class VertxTransactionSupport(val vertx: Vertx) {
private val logger = LogFactory.getLog(javaClass)
fun invokeWithinTransaction(method: Method, targetClass: Class<*>?, invocation: () -> Future<Any?>, txAttr: TransactionAttribute?, tm: VertxTransactionManager): Future<Any?> {
val joinPointIdentification = methodIdentification(method, targetClass, txAttr)
return this.vertx.futureOnContext {
VertxTransactionContextManager.getOrCreateContextHolder(vertx)
VertxTransactionContextManager.getOrCreateContext(vertx)
return@futureOnContext this.createTransactionIfNecessary(tm, txAttr, joinPointIdentification).flatMap { txInfo ->
invocation.invoke()
.recover { t -> this.completeTransactionAfterThrowing(txInfo, t).flatMap { Future.failedFuture(t) } }
.flatMap { result -> this.commitTransactionAfterReturning(txInfo).map { result } }
}
}
}
private fun createTransactionIfNecessary(tm: VertxTransactionManager, txAttr: TransactionAttribute?, joinPointIdentification: String): Future<VertxTransactionInfo?> {
// If no name specified, apply method identification as transaction name.
val useTxAttr = delegatingTransactionAttribute(txAttr, joinPointIdentification)
val tx = if (useTxAttr != null) tm.getVertxTransaction(useTxAttr) else Future.succeededFuture(null)
return tx.map { it -> this.prepareTransactionInfo(tm, useTxAttr, joinPointIdentification, it) }
}
private fun commitTransactionAfterReturning(txInfo: VertxTransactionInfo?): Future<Void> {
if (txInfo != null && txInfo.vertxTransaction != null) {
if (logger.isTraceEnabled) {
logger.trace("Completing transaction for [" + txInfo.joinPointIdentification + "]")
}
return txInfo.transactionManager.commit(txInfo.vertxTransaction!!)
}
return Future.succeededFuture()
}
private fun completeTransactionAfterThrowing(txInfo: VertxTransactionInfo?, t: Throwable): Future<Void> {
if (txInfo != null && txInfo.vertxTransaction != null) {
if (logger.isTraceEnabled) {
logger.trace("Completing transaction for [" + txInfo.joinPointIdentification + "] after exception: " + t)
}
if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(t)) {
return txInfo.transactionManager.rollback(txInfo.vertxTransaction!!).recover {
logger.error("Application exception overridden by rollback exception", t)
if (it is TransactionSystemException) {
it.initApplicationException(t)
} else {
it!!.addSuppressed(t)
}
Future.failedFuture(it)
}
} else {
// We don't roll back on this exception.
// Will still roll back if TransactionStatus.isRollbackOnly() is true.
return txInfo.transactionManager.commit(txInfo.vertxTransaction!!).recover {
logger.error("Application exception overridden by commit exception", t)
if (it is TransactionSystemException) {
it.initApplicationException(t)
} else {
it!!.addSuppressed(t)
}
Future.failedFuture(it)
}
}
}
return Future.succeededFuture()
}
private fun rollbackTransactionOnCancel(txInfo: VertxTransactionInfo?): Future<Void> {
TODO("与completeTransactionAfterThrowing相同")
}
private fun prepareTransactionInfo(tm: VertxTransactionManager, txAttr: TransactionAttribute?, joinPointIdentification: String, transaction: VertxTransaction?): VertxTransactionInfo? {
val txInfo = VertxTransactionInfo(tm, txAttr, joinPointIdentification)
if (txAttr != null) {
// We need a transaction for this method...
if (logger.isTraceEnabled) {
logger.trace("Getting transaction for [" + txInfo.joinPointIdentification + "]")
}
// The transaction manager will flag an error if an incompatible tx already exists.
txInfo.newVertxTransaction(transaction)
} else {
// The TransactionInfo.hasTransaction() method will return false. We created it only
// to preserve the integrity of the ThreadLocal stack maintained in this class.
if (logger.isTraceEnabled) {
logger.trace("Don't need to create transaction for [$joinPointIdentification]: This method isn't transactional.")
}
}
return txInfo
}
}
}
结束
至此,我们已经拥有一个全新的事务管理器,这个事务管理器能支持Vertx Future返回值的目标方法。使用上,跟原本的编程风格一直,只需要更换事务管理器Bean即可。