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 activeJDBC Connection [xxx] will not be managed by Spring
这说明执行B的方法时,当前没有Spring事务上下文,所以连接不会被事务管理,自然不会回滚。而注入未使用的B到A就触发这个问题,可能的原因和解决办法如下:
可能原因排查
事务配置的匹配规则不覆盖B
- 你用Java配置时,
BeanNameAutoProxyCreator设置了setBeanNames("*ServiceImpl"),如果B的类名不是以ServiceImpl结尾(比如叫BService),就不会被事务代理; - XML配置的pointcut是
execution(* root.*.service.*.*(..)),要确认B是否在root.*.service这个包路径下。
- 你用Java配置时,
B的方法不符合事务代理要求
Spring事务代理只对public方法生效,如果B的那个事务方法是private/protected,或者@Transactional注解加在非public方法上,事务不会生效。代理方式冲突
你开启了proxyTargetClass=true(CGLIB代理),如果B实现了接口,可能默认会用JDK动态代理,导致代理链异常;或者A和B的代理生成顺序有问题,导致B的原始实例被提前初始化。
解决步骤
修正事务配置的匹配规则
- Java配置:把
setBeanNames("*ServiceImpl")改成"*Service",或者调整B的类名符合规则; - XML配置:确认pointcut的表达式能覆盖B的方法,比如改成
execution(* com.yourpackage.service..*.*(..))(两个点表示子包也包含)。
- Java配置:把
确保B的方法是public且注解正确
把B的事务方法改成public,并且@Transactional注解加在类上或者public方法上:@Transactional class B{ @Autowired private BDao dao; public void insertAndThrow() { dao.insert(new Entity()); throw new RuntimeException("触发回滚"); } }强制统一代理方式
在Spring Boot配置文件中添加:spring.aop.proxy-target-class=true强制所有Bean用CGLIB代理,避免JDK和CGLIB代理混合导致的问题。
内容的提问来源于stack exchange,提问作者NeB Nep




