为何Spring调用@Transactional(Propagation.NEVER)方法时会创建事务?
场景重现
集成测试代码:
@SpringBootTest(classes = {CarWebServiceApplication.class, CarTestHelper.class}) @Slf4j public class CarRepositoryTest { @Autowired private CarTestHelper carTestHelper; @Test public void aSimpleTest() { carTestHelper.simpleTest(); } }
辅助类代码:
@TestComponent @RequiredArgsConstructor @Slf4j public class CarTestHelper { @Transactional(propagation = Propagation.NEVER, transactionManager ="carTransactionManager") public void simpleTest() { } }
日志配置:
org.springframework.orm.jpa: trace org.springframework.transaction: trace
日志输出:
TRACE 17920 --- [ main]
t.a.AnnotationTransactionAttributeSource : Adding transactional method
'com.example.repository.CarTestHelper.simpleTest' with attribute:
PROPAGATION_NEVER,ISOLATION_DEFAULT; 'carTransactionManager'TRACE 17920 --- [ main] o.s.t.i.TransactionInterceptor :
Getting transaction for
[com.example.repository.CarTestHelper.simpleTest]TRACE 17920 --- [
main] o.s.t.i.TransactionInterceptor : Completing
transaction for [com.example.repository.CarTestHelper.simpleTest]
根据Spring API文档,Propagation.NEVER的定义是:
Execute non-transactionally, throw an exception if a transaction exists.
疑惑:日志显示方法被标记为事务方法,且执行了获取和完成事务的操作,这是否与文档冲突?
解答
1. 日志中的“获取事务”并非创建事务
Spring事务拦截器的Getting transaction for日志,只是表示它在执行事务属性的处理逻辑,并非真的创建了新事务。对于PROPAGATION_NEVER来说,这个步骤的核心是:
- 检查当前线程是否存在活跃的事务
- 如果存在,直接抛出
IllegalTransactionStateException - 如果不存在,就直接进入方法执行,全程没有任何事务上下文
2. “完成事务”只是拦截器的标准流程
日志中的Completing transaction for是事务拦截器的统一收尾逻辑,无论当前方法是否真的处于事务中,拦截器都会执行这个步骤。对于PROPAGATION_NEVER的情况,这个步骤只是做一些清理工作,没有实际的事务提交或回滚操作。
3. API文档描述准确,只是日志容易造成误解
文档的描述完全正确:PROPAGATION_NEVER确实是无事务执行,且如果有现存事务就抛出异常。日志里的流程只是Spring事务拦截器处理所有事务属性时的统一框架流程,并不代表实际创建了事务。
你可以测试验证:在调用simpleTest()之前手动开启一个事务,此时就会触发预期的异常,完全符合文档定义。
内容的提问来源于stack exchange,提问作者James

