Spring显示声明事务失效

Spring事务原理

Spring 事务的实现基于AOP(面向切面编程) 和事务管理器(TransactionManager),核心是通过代理机制对目标方法进行环绕增强,在方法执行前后嵌入事务的开启、提交、回滚等逻辑

  1. 当调用被@Transactional注解标记的方法时,实际执行的是代理对象的方法。
  2. 代理对象会先触发事务拦截器(TransactionInterceptor),由拦截器完成事务的开启、提交或回滚。
  3. 事务拦截器的逻辑由事务管理器(TransactionManager) 实现,不同的数据源(如 JDBC、Hibernate、MyBatis)对应不同的事务管理器。

Spring事务失效场景

1.自调用

事务通过代理对象触发,同一类内方法调用时使用this,未经过代理,导致事务逻辑不生效。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Service
public class UserServiceImpl implements UserService {

public void transfer(User user) {
// this.doSomething(fromId, toId, amount);
doSomething(user);
// UserService proxy = (UserService) AopContext.currentProxy();
// proxy.doSomething(user);
}

@Transactional
public void doSomething(User user) {
userMapper.insert(user);
}
}

2.非 public方法使用 @Transactional

Spring 的事务管理基于 AOP 动态代理实现,而默认的代理逻辑(如SpringAopProxy)仅对public方法进行事务增强。这是因为 Java 语言规范中,非 public 方法的可见性限制导致代理无法有效拦截,最终事务注解被忽略。
Spring 约定

1
2
3
4
5
6
7
8
@Service
public class UserServiceImpl implements UserService {

@Transactional
private void test(User user) {
userMapper.insert(user);
}
}

3.异常被捕获且未重新抛出

Spring 事务默认通过未捕获的异常触发回滚触发,若异常被 try-catch 捕获且未抛出,事务管理器认为执行成功,不会回滚。

1
2
3
4
5
6
7
8
9
10
11
12
13
@Service
public class UserServiceImpl implements UserService {

@Transactional
public void test(User user) {
try {
userMapper.insert(user);
} catch (Exception e) {
// 捕获异常但未抛出,事务管理器感知不到
System.out.println("发生异常:" + e.getMessage());
}
}
}

4.异常类型不匹配导致事务失效

默认仅对 RuntimeException 和 Error 回滚,若抛出Checked Exception(如 IOException)且未通过 rollbackFor 指定,事务不回滚。

1
2
3
4
5
6
7
8
9
10
@Service
public class UserServiceImpl implements UserService {
// 默认只回滚 RuntimeException,此处抛出受检异常
@Transactional
public void test(User user) throws IOException {
userMapper.insert(user);
// 抛出受检异常(IOException),默认不回滚
throw new IOException("模拟受检异常");
}
}

5.目标类未被 Spring 管理

事务依赖 Spring 容器生成的代理对象,若通过 new 手动创建对象,无代理增强,注解失效。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Service
public class UserServiceImpl implements UserService {
@Transactional
public void test(User user) {
userMapper.insert(user);
}
}

@RestController
public class TestController {
@GetMapping("/test")
public String test(User user) {
UserService userService = new UserService();
userService.test(user);
}
}

小结

Spring 事务失效的本质是 ​调用路径没有经过事务代理。理解 AOP 代理机制是解决所有事务失效问题的关键。在实际开发中,​同类自调用和异常被捕获是最容易踩坑的两个场景。