You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring WebFlux多数据源:能否在AbstractRoutingConnectionFactory中使用事务属性?

解决方案:基于包名的R2dbc多数据源路由适配

在Spring WebFlux响应式环境下,你当前的实现存在两个核心问题:一是TransactionSynchronizationManager.getCurrentTransactionName()默认仅在显式指定@Transactional(name)时才有值,二是响应式上下文依赖Reactor Context而非ThreadLocal传递,直接通过TransactionSynchronizationManager无法拿到事务方法的包名信息。以下是可落地的改造方案:


1. 自定义事务管理器,将包名注入Reactor Context

重写R2dbcTransactionManager的doBegin方法,从事务定义中提取方法全限定名并解析包名,存入Reactor Context供后续路由使用:

public class ContextAwareR2dbcTransactionManager extends R2dbcTransactionManager {

    @Override
    protected Mono<Void> doBegin(TransactionSynchronizationManager synchronizationManager, Object transaction, TransactionDefinition definition) {
        String transactionName = definition.getName();
        // 若未指定@Transactional(name),从TransactionAttribute提取方法全限定名
        if (transactionName == null && definition instanceof TransactionAttribute) {
            TransactionAttribute attr = (TransactionAttribute) definition;
            Method targetMethod = attr.getTargetMethod();
            if (targetMethod != null) {
                transactionName = targetMethod.getDeclaringClass().getName() + "." + targetMethod.getName();
            }
        }

        // 解析包名并存入Reactor Context
        String packageName = extractPackage(transactionName);
        return super.doBegin(synchronizationManager, transaction, definition)
                .contextWrite(ctx -> ctx.put("TX_METHOD_PACKAGE", packageName));
    }

    private String extractPackage(String fullMethodName) {
        if (fullMethodName == null) return null;
        int lastDot = fullMethodName.lastIndexOf('.');
        return lastDot != -1 ? fullMethodName.substring(0, lastDot) : null;
    }
}

2. 修改路由连接工厂,从Context获取包名

调整CustomRoutingConnectionFactory的determineCurrentLookupKey方法,直接从Reactor Context中读取预存的包名,实现数据源路由:

public class CustomRoutingConnectionFactory extends AbstractRoutingConnectionFactory {

    @Override
    protected Mono<Object> determineCurrentLookupKey() {
        return Mono.deferContextual(ctxView -> {
            String packageName = ctxView.getOrDefault("TX_METHOD_PACKAGE", null);
            if (packageName == null) {
                return Mono.error(new RuntimeException("No transaction method package found in context"));
            }

            if (packageName.startsWith(SomeR2dbcProperties.BASE_PACKAGE)) {
                return Mono.just(SomeR2dbcProperties.KEY);
            } else if (packageName.startsWith(OtherR2dbcProperties.BASE_PACKAGE)) {
                return Mono.just(OtherR2dbcProperties.KEY);
            }
            return Mono.error(new RuntimeException("Not supported package: " + packageName));
        });
    }

    // 原有其他代码...
}

3. 配置替换默认组件

在配置类中注册自定义事务管理器和路由连接工厂,确保上下文传递生效:

@Configuration
public class R2dbcConfig {

    @Bean
    public R2dbcTransactionManager r2dbcTransactionManager(ConnectionFactory connectionFactory) {
        return new ContextAwareR2dbcTransactionManager(connectionFactory);
    }

    @Bean
    public CustomRoutingConnectionFactory routingConnectionFactory(
            @Qualifier("someConnFactory") ConnectionFactory someConnFactory,
            @Qualifier("otherConnFactory") ConnectionFactory otherConnFactory) {
        CustomRoutingConnectionFactory routingFactory = new CustomRoutingConnectionFactory();
        Map<Object, Object> targetFactories = new HashMap<>();
        targetFactories.put(SomeR2dbcProperties.KEY, someConnFactory);
        targetFactories.put(OtherR2dbcProperties.KEY, otherConnFactory);
        
        routingFactory.setTargetConnectionFactories(targetFactories);
        routingFactory.setDefaultTargetConnectionFactory(someConnFactory); // 设置默认数据源
        return routingFactory;
    }

    // 注册具体的Some/Other ConnectionFactory...
}

补充说明

  • 使用startsWith而非contains匹配包名,避免不同包名因包含相同字符串导致误路由;
  • 若需支持非事务场景的路由,可在Repository层方法中手动将包名存入Reactor Context,在determineCurrentLookupKey中兼容处理;
  • 确保@Transactional注解应用在业务层方法上,保证方法全限定名能被正确提取。

内容的提问来源于stack exchange,提问作者KimDoubleB

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 20:19:01