在基于 Spring Framework 开发项目时,大多数情况下,如果一个业务需要事务管理,我们会在 Service 层的实现方法上添加 @Transactional
注解,Spring 会自动给对应的方法添加事务管理。
如果我们在一个包含事务的方法中,调用了另一个包含里事务的方法,那么,Spring 会创建新的事物吗?如果方法抛出异常导致事务会滚了呢?
这要分两种情况分析。
第一种,是在一个包含事务的方法中,通过直接使用方法名或者 this.方法名
调用了当前类中的另一个包含事务的方法,这时,Spring 不会创建新的事务,原因要从 Spring 实现事务管理的原理说起。
Spring 给添加了 @Transactional
注解的方法自动添加事务管理,是通过 AOP 实现的,Spring 通过对当前的方法进行增强,来提供了事务的开启、提交、会滚的逻辑。而 AOP 的实现,要是用到动态代理。
也就是说,在 Spring 的环境中,一个包含事务的方法被调用,其实是代理类的方法被调用了,只有这样才会被开启事务,但是在方法体中调用的同一个类中的其他方法,其调用的都是这个类本身的方法,而不是代理类的方法,因此,被调用的方法不会被增强。
所以结论就是,这种情况下,不会创建新的事务。
第二种,是在一个包含了事务的方法中,通过一个依赖的 Bean,调用了其他类的包含事务的方法。此时,因为是通过依赖的 Bean 调用的,Bean 被 Spring 的容器所管理,因此,实际调用的是增强后的代理类,会执行增强后的逻辑,但是,当前已经开启了一个事务,Spring 会如何处理呢?
此时就要提到 Spring 事务的传播特性,在 @Transactional
注解中有一个 propagation 属性,事务的传播特性可以通过它来指定。可选的值可以在 TransactionDefinition
中找到,分别是一下这些:
PROPAGATION_REQUIRED
:当前有事务就使用当前事务,否则创建新的事务。(默认值)PROPAGATION_SUPPORTS
:事务不是必需的。PROPAGATION_MANDATORY
:如果当前没有事务则抛出异常。PROPAGATION_REQUIRES_NEW
:无论当前是否有事务,都创建新的事务。PROPAGATION_NOT_SUPPORTED
:不支持事务,按照非事务方式运行。PROPAGATION_NEVER
:不支持事务,如果有事务则抛出异常。PROPAGATION_NESTED
:如果当前有事务就在当前事务里再创建一个事务。
举个例子,当这个值为 REQUIRES_NEW 时,代表无论是否调用方(方法A)是否开启了事务,被调用方(方法B)都会开启新的事务。此时,如果方法B的事务出错发生了会滚,那么只会会滚方法B的事务,方法A的事务不受影响。