【Spring Vertx 响应式事务,spring-tx-reactive-vertx】

Spring Vertx 响应式事务,spring-tx-reactive-vertx

背景

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包裹起来。

拦截流程

  1. 探测出事务方法的事务配置 TransactionAttribute
  2. 探测出事务方法应该使用的是哪zhTransactionManager
  3. 构建 VertxTransactionSupport 包裹事务方法(适用于Vertx Future的事务)
  4. 执行 VertxTransactionSupport 对事务方法的事务环绕增强

事务执行流程

  1. 生成事务方法的唯一标识
  2. 构建Vertx上下文,让代码运行在同一个Vertx上下文中
  3. 构建事务上下文,VertxTransactionContext、VertxTransactionContextHolder
  4. 创建事务对象或者获取事务对象
  5. 执行目标方法
  6. 事务提交/事务回滚

这其中,会调用 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即可。

相关推荐
罗山仔3 小时前
【Vertx构建异步响应式reactive mybatis,mybatis-vertx-adaptor】
mybatis·orm·异步·reactive·响应式·webflux·vertx
小跘an吻纸3 天前
Vue 3 Composition API实战
typescript·前端框架·vue·响应式
木斯佳17 天前
前端八股文面经大全:小红书前端一二面OC(下)·(2026-03-17)·面经深度解析
前端·vue3·proxy·八股·响应式
Change!!3 个月前
富文本输入后带上了拼音/英文字母
富文本·响应式·js防抖
rqtz3 个月前
网页响应式布局方法
前端·css·响应式
闲人编程7 个月前
前端形态与样式风格:从古典到现代的视觉语言演进
前端·css·状态模式·组件·js·风格·响应式
k↑1 年前
物联网之使用Vertx实现MQTT-Server最佳实践【响应式】
物联网·mqtt·微服务·响应式
zhanggongzichu1 年前
跨端兼容——请让我的页面展现在电脑、平板、手机上
前端·css·vue·自适应·响应式·跨端兼容
组合缺一1 年前
Solon Cloud Gateway 开发:导引
java·gateway·reactor·solon·响应式