Spring声明式事务原理
Spring声明式事务是基于AOP(面向切面编程)实现的。其核心流程如下:
1.事务管理器配置:
在Spring中,首先需要配置事务管理器,例如DataSourceTransactionManager(针对基于JDBC数据源的事务管理) 。它负责管理事务的开始、提交、回滚等操作,是事务管理的核心组件。
2.AOP切面织入:
Spring通过AOP机制,在方法调用前后织入事务相关逻辑。当一个标注了事务注解(如@Transactional)的方法被调用时,Spring会创建一个代理对象(基于JDK动态代理或CGLIB代理,取决于目标类是否实现接口 )。这个代理对象会在实际方法调用之前,调用事务管理器的begin()方法来开启事务;在方法正常执行结束后,调用事务管理器的commit()方法提交事务;如果方法执行过程中抛出异常,调用事务管理器的rollback()方法回滚事务。
3.传播行为与隔离级别处理:
@Transactional注解中可以指定事务传播行为(如REQUIRED、REQUIRES_NEW等)和隔离级别(如READ_COMMITTED、SERIALIZABLE等)。事务管理器会根据这些配置来正确处理事务之间的关系以及并发访问时的数据一致性问题。
事务失效场景
1.方法非public:@Transactional注解只能应用在public修饰的方法上。如果标注在private、protected或默认访问修饰符的方法上,事务不会生效。这是因为Spring的AOP代理机制在处理事务时,是基于Java的访问控制规则,非public方法无法被代理增强。
2.自调用问题:当一个类中的方法A调用同一类中的另一个被@Transactional标注的方法B时,事务不会生效。这是因为在这种情况下,没有经过代理对象调用,而是直接在目标对象内部调用,跳过了AOP的事务增强逻辑。例如:
typescript
@Service
public class UserService {
@Transactional
public void methodA() {
methodB();
}
@Transactional
public void methodB() {
// 业务逻辑
}
}
这里methodB的事务不会生效。
3. 未被Spring容器管理:如果一个类没有被Spring容器管理(没有使用@Component、@Service等注解将其纳入Spring容器),即使方法上标注了@Transactional,事务也不会生效,因为不存在Spring创建的代理对象来织入事务逻辑。
4. 异常被捕获处理:如果在被@Transactional标注的方法内部捕获了异常,并且没有重新抛出,事务不会回滚。例如:
typescript
@Service
public class OrderService {
@Transactional
public void createOrder() {
try {
// 可能抛出异常的业务逻辑
// 假设这里执行数据库插入操作出错
} catch (Exception e) {
// 捕获异常但未重新抛出
e.printStackTrace();
}
}
}
此时事务不会回滚,数据库操作会提交。
5.用的是mysql的MYISAM,这个引擎本身不支持事务。
6.rollbackFor没有设置对,比如默认没有任何配置,则方法内抛出IOException则不会回滚,需要配置@Transactional(rollbackFor=Exception.class)
7.@Transactional应用在final和static方法上,因为aop 默认是cglib代理(apringboot2.X开始),无法对final方法子类化。static是静态方法,属于类,不属于实例对象,无法被代理。
8.propagation传播机制配置错误。因为配置了Propagation.REQUIRES_NEW,是新启一个事务.因此2个事务之间是无法保证数据的一致性的。
java
public class OrderService {
@Transactional(propagation=Propagation.REQUIRES_NEW)
public void addOrderAndUser(int id) {
try {
userMapper.addUser(id);
orderMapper.addOrder(id);
// 可能抛出异常的业务逻辑
// 假设这里执行数据库插入操作出错
} catch (Exception e) {
// 捕获异常但未重新抛出
e.printStackTrace();
}
}
@Transactional(propagation=Propagation.REQUIRES_NEW)
public void addOrder(int id) {
try {
orderMapper.save(id);
// 可能抛出异常的业务逻辑
// 假设这里执行数据库插入操作出错
} catch (Exception e) {
// 捕获异常但未重新抛出
e.printStackTrace();
}
}
}