You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Spring Boot事务配置失败问题及非托管类事务管理咨询

问题分析与解决方案

咱们先从你定位到的核心问题入手,再解决那个注入未使用Bean导致事务失效的奇怪场景:


一、核心问题:非Spring托管类C中注入的A无法生效事务

你提到的C是继承外部类且未被Spring托管的类,里面用@Autowired注入Spring Bean A,这确实是事务失效的关键原因:

Spring的依赖注入和事务代理都是基于容器管理的Bean实现的,非Spring托管的类(比如你手动new C()创建的实例)不会被Spring处理,@Autowired注解在这里根本不会生效——要么a是null,要么即使你通过某种方式拿到了A的实例,也不是Spring生成的事务代理对象,自然无法触发事务逻辑。

解决办法

根据你的场景,有两种可行方案:

1. 手动从Spring容器获取代理后的A实例

可以先实现一个Spring上下文持有类,用来获取容器中的Bean:

@Component
public class SpringContextHolder implements ApplicationContextAware {
    private static ApplicationContext context;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        context = applicationContext;
    }

    public static <T> T getBean(Class<T> clazz) {
        return context.getBean(clazz);
    }
}

然后在C中手动获取A的代理实例:

public class C extends ExternalClass { 
    private A a;

    public C() {
        // 获取Spring代理后的A实例,事务能正常生效
        this.a = SpringContextHolder.getBean(A.class);
    }

    // doSomething... 
}

2. 将C转为Spring托管Bean(如果业务允许)

如果C不需要手动new,可以给它加上@Component/@Service注解,或者在配置类中用@Bean声明:

@Component
public class C extends ExternalClass { 
    @Autowired 
    private A a; 

    // doSomething... 
}

这样Spring会自动处理@Autowired,注入的A是事务代理对象,事务逻辑就能正常工作了。


二、A注入未使用的B导致B的事务失效问题

从你提供的日志可以看到关键异常信号:

SqlSession [xxx] was not registered for synchronization because synchronization is not active
JDBC Connection [xxx] will not be managed by Spring

这说明执行B的方法时,当前没有Spring事务上下文,所以连接不会被事务管理,自然不会回滚。而注入未使用的B到A就触发这个问题,可能的原因和解决办法如下:

可能原因排查

  1. 事务配置的匹配规则不覆盖B

    • 你用Java配置时,BeanNameAutoProxyCreator设置了setBeanNames("*ServiceImpl"),如果B的类名不是以ServiceImpl结尾(比如叫BService),就不会被事务代理;
    • XML配置的pointcut是execution(* root.*.service.*.*(..)),要确认B是否在root.*.service这个包路径下。
  2. B的方法不符合事务代理要求
    Spring事务代理只对public方法生效,如果B的那个事务方法是private/protected,或者@Transactional注解加在非public方法上,事务不会生效。

  3. 代理方式冲突
    你开启了proxyTargetClass=true(CGLIB代理),如果B实现了接口,可能默认会用JDK动态代理,导致代理链异常;或者A和B的代理生成顺序有问题,导致B的原始实例被提前初始化。

解决步骤

  1. 修正事务配置的匹配规则

    • Java配置:把setBeanNames("*ServiceImpl")改成"*Service",或者调整B的类名符合规则;
    • XML配置:确认pointcut的表达式能覆盖B的方法,比如改成execution(* com.yourpackage.service..*.*(..))(两个点表示子包也包含)。
  2. 确保B的方法是public且注解正确
    把B的事务方法改成public,并且@Transactional注解加在类上或者public方法上:

    @Transactional
    class B{ 
        @Autowired 
        private BDao dao; 
    
        public void insertAndThrow() { 
            dao.insert(new Entity()); 
            throw new RuntimeException("触发回滚");
        } 
    }
    
  3. 强制统一代理方式
    在Spring Boot配置文件中添加:

    spring.aop.proxy-target-class=true
    

    强制所有Bean用CGLIB代理,避免JDK和CGLIB代理混合导致的问题。


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

火山引擎 最新活动