分享两个我在学习spring中遇到问题以及解答
Spring中的事务是如何实现的
1. Spring事务底层是基于数据库事务和AOP机制的实现的
2.首先对于使用了@Transactiona注解的Bean, Spring会创建一个代理对象作为Bean
3.当调用代理对象的方法时,会先判断该方法上是否加了@Transactional注解
4.如果加了,那么则利用事务管理器(transactionManager)创建一个数据库连接
5.并且修改数据库连接的autocommit属性为false,禁止此连接的自动提交,这是实现Spring事务非常重要的一步
6.然后执行当前方法,方法中会执行sql
7.执行完当前方法后,如果没有出现异常就直接提交事务
8.如果出现了异常,并且这个异常是需要回滚的就会回滚事务,否则仍然提交事务
什么时候@transaction注解会失效?
1.因为Spring事务是基于代理来实现的,所以某个加了@Transaction的方法只有是被代理对象调用时,那么这个注解才会生效,所以如果是被代理对象来调用这个方法,那么@Transaction是不会生效的。
代理基础
在Spring框架中,事务管理通过使用动态代理实现。这意味着当你在一个类的方法上加上 @Transactional 注解时,Spring运行时会创建这个类的一个代理(Proxy),并在这个代理类中包装原始类的事务逻辑。实际上,当你调用一个被 @Transactional 注解的方法时,你是在调用一个代理对象的方法,这个代理对象在调用真实对象的方法之前和之后,会分别处理事务的开始和结束。
自调用问题
当一个代理对象的方法内部调用同一对象的另一个 @Transactional 方法时,事务注解会失效。这是因为代理的事务管理逻辑只在从代理对象到目标对象的方法调用过程中起作用。如果是在目标对象内部的方法间调用(也称为自调用),则调用不会经过代理对象,因此不会执行代理中的事务处理逻辑。
具体例子
假设有一个服务类 BookService,其中包含两个方法:updateBookPrice 和 updateBookStatus,两者都被 @Transactional 注解:
@Service
public class BookService {
@Transactional
public void updateBookPrice(Long bookId, BigDecimal newPrice) {
// 更新书籍价格的逻辑
updateBookStatus(bookId, "UPDATED"); // 自调用
}
@Transactional
public void updateBookStatus(Long bookId, String status) {
// 更新书籍状态的逻辑
}
}
在上述代码中,当从外部调用 updateBookPrice 方法时,由于这个方法被 @Transactional 注解,Spring会通过代理来管理事务。然而,在 updateBookPrice 方法内部调用 updateBookStatus 方法时,由于是在同一个实例内部的直接方法调用,这个调用不会经过代理,因此 updateBookStatus 方法上的 @Transactional 注解不会有任何效果,事务管理逻辑不会被执行。
2.同时如果某个方法是private的,那么@Transaction也会失效,因为底层cglib是基于父子类来实现的,子类是不能重载父类的private方法的,所以无法很好的利用代理,也会导致@Transaction失效。
3.还有一个的话就是rollbackFor属性,如果你报的异常不是rollback所指定的异常的话其实它的注解也会失效。因为并没有进行回滚