@Transactional 是 Spring 框架中用于声明式事务管理的注解,其核心原理基于 Spring AOP(面向切面编程)和动态代理机制。下面详细解释其工作原理:
1. 核心机制:AOP 与动态代理
-
AOP(面向切面编程) :Spring 通过 AOP 在目标方法执行前后插入事务管理逻辑(如开启、提交或回滚事务),而不需要修改业务代码。
-
动态代理:Spring 支持两种代理方式:
- JDK 动态代理:基于接口实现,代理对象必须实现至少一个接口。
- CGLIB 代理:基于继承实现,通过生成子类覆盖目标方法。
2. 事务管理器(PlatformTransactionManager)
-
Spring 通过
PlatformTransactionManager接口管理事务,不同的数据访问技术有不同的实现:- JDBC:使用
DataSourceTransactionManager。 - JPA:使用
JpaTransactionManager。 - Hibernate:使用
HibernateTransactionManager。
- JDBC:使用
3. 工作流程
-
解析注解:
- Spring 在启动时通过
@EnableTransactionManagement或 XML 配置启用事务支持。 - 通过
@Transactional注解标记需要事务管理的方法或类(类级别表示所有方法默认有事务)。
- Spring 在启动时通过
-
创建代理对象:
- Spring 扫描到
@Transactional注解后,为目标类创建代理对象。
- Spring 扫描到
-
拦截方法调用:
- 当调用被
@Transactional标记的方法时,代理对象会拦截该调用。
- 当调用被
-
事务管理逻辑:
-
开启事务:获取数据库连接,设置隔离级别和传播行为。
-
执行目标方法:调用实际业务逻辑。
-
提交或回滚:
- 若方法正常返回,提交事务。
- 若抛出异常,根据
rollbackFor和noRollbackFor配置决定是否回滚(默认回滚RuntimeException和Error)。
-
4. 关键属性
-
propagation(传播行为) :定义事务如何传播,例如:
REQUIRED(默认):如果当前存在事务,则加入;否则创建新事务。REQUIRES_NEW:总是创建新事务,挂起当前事务(如果存在)。SUPPORTS:支持当前事务,若无则以非事务方式执行。
-
isolation(隔离级别) :定义事务的隔离程度,如
READ_COMMITTED、SERIALIZABLE等。 -
rollbackFor:指定哪些异常触发回滚(默认仅运行时异常)。
-
timeout:事务超时时间。
-
readOnly:标记为只读事务,可优化性能。
5. 示例代码
java
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void transferMoney(Long fromId, Long toId, BigDecimal amount) {
// 从账户扣款
User fromUser = userRepository.findById(fromId).orElseThrow();
fromUser.setBalance(fromUser.getBalance().subtract(amount));
userRepository.save(fromUser);
// 模拟异常
if (true) throw new RuntimeException("模拟转账失败");
// 转入账户
User toUser = userRepository.findById(toId).orElseThrow();
toUser.setBalance(toUser.getBalance().add(amount));
userRepository.save(toUser);
}
}
- 执行结果:由于抛出
RuntimeException,整个事务回滚,两个账户的余额均不变。
6. 注意事项
-
自调用问题:
- 同一个类中方法 A 调用带
@Transactional的方法 B,事务不会生效,因为没有通过代理对象调用。 - 解决方案:通过
ApplicationContext或AopContext获取代理对象。
- 同一个类中方法 A 调用带
-
异常处理:
- 若方法内部捕获异常但未重新抛出,事务不会回滚。
- 需确保异常传播到代理对象,触发事务回滚逻辑。
-
代理模式选择:
- 若目标类未实现接口,Spring 默认使用 CGLIB 代理。
- 若需强制使用 JDK 代理,可配置
proxyTargetClass = false。
7. 底层实现简化示例
下面是 Spring 代理对象的简化逻辑(仅作原理示意):
java
public class TransactionalProxy {
private final Object target; // 目标对象
private final PlatformTransactionManager transactionManager;
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 1. 检查方法是否有 @Transactional 注解
if (method.isAnnotationPresent(Transactional.class)) {
TransactionStatus status = null;
try {
// 2. 开启事务
status = transactionManager.getTransaction(new DefaultTransactionDefinition());
// 3. 执行目标方法
Object result = method.invoke(target, args);
// 4. 提交事务
transactionManager.commit(status);
return result;
} catch (Exception e) {
// 5. 回滚事务(根据异常类型)
if (shouldRollbackOn(e)) {
transactionManager.rollback(status);
}
throw e;
}
}
// 无 @Transactional 注解,直接调用目标方法
return method.invoke(target, args);
}
}
总结
@Transactional 通过 Spring AOP 和动态代理,在方法执行前后自动管理事务,实现了事务管理与业务逻辑的解耦。其核心在于代理对象拦截方法调用,根据注解配置控制事务的开启、提交和回滚。使用时需注意自调用问题和异常处理,确保事务按预期生效。