Spring声明式事务源码全链路剖析与设计模式深度解读


Spring声明式事务源码全链路剖析与设计模式深度解读

作者:AI
日期:2025-05-22


一、前言

Spring事务是企业级开发的基石,但“为什么有时事务失效?”、“不同传播行为背后发生了什么?”、“Spring事务源码到底如何实现?”这些问题困扰着无数开发者。本文将从源码行级剖析事务传播机制全链路跟踪设计模式变体实现三个层面,彻底解构Spring声明式事务体系。


二、Spring声明式事务核心源码全链路

2.1 事务AOP代理的生成(代理模式)

Spring通过AOP机制实现声明式事务,核心逻辑在于Bean初始化时判断是否需要代理,并生成代理对象。

源码片段:AbstractAutoProxyCreator#wrapIfNecessary

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {// ...是否需要事务增强判断...Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));return proxy;
}

解读:

  • 满足条件即生成JDK/CGLIB代理,后续所有对该Bean的调用都会被代理拦截。
  • 这就是经典的代理模式应用。

2.2 进入AOP拦截链(责任链模式)

代理对象的方法调用被拦截后,会进入AOP拦截器链,事务拦截器只是其中之一。

源码片段:CglibAopProxy.DynamicAdvisedInterceptor#intercept

public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);if (chain.isEmpty()) {return methodProxy.invoke(target, args);} else {return new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();}
}

解读:

  • 多个拦截器顺序执行,责任链模式的典范。
  • TransactionInterceptor在链中负责事务处理。

2.3 事务拦截器主流程(模板方法+责任链)

源码片段:TransactionInterceptor#invoke

public Object invoke(final MethodInvocation invocation) throws Throwable {TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);PlatformTransactionManager tm = determineTransactionManager(txAttr);String joinpointIdentification = methodIdentification(method, targetClass, txAttr);TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);Object retVal;try {retVal = invocation.proceedWithInvocation();} catch (Throwable ex) {completeTransactionAfterThrowing(txInfo, ex);throw ex;} finally {cleanupTransactionInfo(txInfo);}commitTransactionAfterReturning(txInfo);return retVal;
}

解读:

  • 事务的开启、提交、回滚、清理均在此流程完成,标准的模板方法模式
  • 业务代码通过proceedWithInvocation()继续责任链。

2.4 事务管理器与策略模式

源码片段:TransactionAspectSupport#createTransactionIfNecessary

protected TransactionInfo createTransactionIfNecessary(PlatformTransactionManager tm, TransactionAttribute txAttr, String joinpointIdentification) {if (txAttr != null && tm != null) {TransactionStatus status = tm.getTransaction(txAttr);return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);}// ...
}

解读:

  • PlatformTransactionManager是策略接口,支持多种实现(JDBC、JPA、JTA等)。
  • 运行时根据配置选择合适的策略。

2.5 事务传播机制源码全链路

传播行为决策入口

源码片段:AbstractPlatformTransactionManager#getTransaction

public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {Object transaction = doGetTransaction(); // 线程级事务资源if (isExistingTransaction(transaction)) {return handleExistingTransaction(definition, transaction, debugEnabled);}// ...无事务的处理...
}
核心传播分支源码

源码片段:handleExistingTransaction(部分)

if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {Object suspendedResources = suspend(transaction);try {return startTransaction(definition, transaction, debugEnabled, suspendedResources);} catch (RuntimeException | Error beginEx) {resumeAfterBeginException(transaction, suspendedResources, beginEx);throw beginEx;}
}
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {DefaultTransactionStatus status = prepareTransactionStatus(...);status.createAndHoldSavepoint();return status;
}
// ...其它分支...

解读:

  • REQUIRES_NEW:挂起原事务、新建事务。
  • NESTED:创建JDBC保存点,支持嵌套回滚。
  • 其它传播行为:详见下表。

2.6 各传播行为源码链路与执行表

传播类型现有事务行为源码分支位置
REQUIRED加入现有事务handleExistingTransaction 默认分支
创建新事务getTransaction, startTransaction
REQUIRES_NEW挂起原事务,创建新事务handleExistingTransaction, suspend
创建新事务getTransaction, startTransaction
SUPPORTS加入现有事务handleExistingTransaction 默认分支
非事务性执行getTransaction, prepareTransactionStatus
NOT_SUPPORTED挂起原事务,非事务性执行handleExistingTransaction, suspend
非事务性执行getTransaction, prepareTransactionStatus
NEVER抛异常handleExistingTransaction, throw
非事务性执行getTransaction, prepareTransactionStatus
MANDATORY加入现有事务handleExistingTransaction 默认分支
抛异常getTransaction, throw
NESTED创建保存点,嵌套事务handleExistingTransaction, createAndHoldSavepoint
创建新事务getTransaction, startTransaction

2.7 事务挂起、恢复与保存点(进阶细节)

源码片段:挂起/恢复/保存点

protected final SuspendedResourcesHolder suspend(@Nullable Object transaction) {if (transaction != null) {doSuspend(transaction); // 释放当前事务资源}List<TransactionSynchronization> suspendedSynchronizations = doSuspendSynchronization();// 返回SuspendedResourcesHolder
}protected final void resume(@Nullable Object transaction, @Nullable SuspendedResourcesHolder resourcesHolder) {if (resourcesHolder != null) {doResume(transaction, resourcesHolder.suspendedResources);}doResumeSynchronization(resourcesHolder.suspendedSynchronizations);
}DefaultTransactionStatus status = prepareTransactionStatus(...);
status.createAndHoldSavepoint(); // JDBC Savepoint

解读:

  • 挂起/恢复用于REQUIRES_NEW、NOT_SUPPORTED等。
  • NESTED通过保存点实现内层回滚而不影响外层。

2.8 事务提交/回滚与异常判定

源码片段:事务回滚判定/提交

protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {if (txInfo != null && txInfo.getTransactionStatus() != null) {if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());} else {txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());}}
}

解读:

  • 默认只对RuntimeExceptionError回滚。
  • rollbackFor/noRollbackFor优先处理。

三、所有事务传播场景全链路源码跟踪

3.1 场景举例(REQUIRES_NEW)

假设A方法有事务,B方法@Transactional(propagation = REQUIRES_NEW),A调用B:

  1. 进入B代理
  2. TransactionInterceptor#invoke
  3. createTransactionIfNecessary -> getTransaction
  4. 发现已有事务,handleExistingTransaction
  5. 判断为REQUIRES_NEW,suspend挂起A的事务
  6. startTransaction新建B的事务
  7. 执行B
  8. B事务commit/rollback
  9. resume恢复A的事务

其他传播行为可类推,详见上表。


四、设计模式在Spring事务中的变体实现

  • 代理模式:AOP代理对象生成与调用拦截
  • 策略模式PlatformTransactionManager多实现切换
  • 模板方法模式AbstractPlatformTransactionManager定义流程,子类实现细节
  • 责任链模式:AOP拦截器链,事务拦截器只是其中一环
  • 适配器模式:JDBC、JPA、JTA等适配到统一事务接口
  • 装饰器/观察者变体TransactionSynchronizationManager扩展事务回调

五、常见误区与源码分析

5.1 内部方法调用事务失效

@Service
public class A {@Transactionalpublic void methodA() { methodB(); }@Transactionalpublic void methodB() { /* ... */ }
}

分析:

  • methodB()为内部调用,未经过AOP代理,不会增强事务。

解决方案:

  • 通过外部Bean注入调用或自我注入。

六、实用速查与小结

  • 代理横切事务,策略适配场景,模板规范流程,责任链环环相扣,适配器统一接口。
  • 传播行为定范围,异常回滚看类型,自定义优先于默认。
  • 源码每一行都映射着设计思想,理解源码才能知其所以然。

七、参考资料

  • Spring事务源码官方GitHub
  • Spring官方事务文档
  • Spring源码深度解析

八、结语

Spring事务的本质,是AOP+多设计模式联合作用的工程艺术。理解每一行源码,追踪每一个传播行为的全链路,能让你真正掌控事务边界与一致性。
知其然,更知其所以然。


欢迎收藏、点赞、转发,有问题欢迎评论区一起深挖Spring源码!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/diannao/84610.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

云原生安全基础:深入探讨容器化环境中的权限隔离与加固策略

&#x1f525;「炎码工坊」技术弹药已装填&#xff01; 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 在云原生环境中&#xff0c;容器化技术&#xff08;如 Docker 和 Kubernetes&#xff09;的广泛应用带来了灵活性与效率&#xff0c;但也引入了新的安全挑…

如何在 ONLYOFFICE 演示文稿中调整段落首行缩进

在制作演示文稿时&#xff0c;保持内容的一致性与可读性至关重要&#xff0c;而段落首行缩进作为格式设置的关键环节&#xff0c;直接影响着整体呈现效果。在本文中&#xff0c;我们将介绍如何通过创建 ONLYOFFICE 宏&#xff0c;快速设置演示文稿中所有段落的首行缩进。 关于 …

[Asp.Net]GridView、Repeater 导出Excel长数字显示成科学计数

类似身份证纯数字的格式时 &#xff0c;excel默认是数字格式 变成了科学计数法 &#xff0c; GridView:RowDataBound 添加e.Row.Cells[2].Attributes.Add(“style”, “vnd.ms-excel.numberformat:;”); protected void GridView1_RowDataBound(object sender, GridViewRowE…

Ntfs!NtfsReadBootSector函数分析之nt!CcGetVacbMiss中得到一个nt!_VACB结构

第一部分&#xff1a; 1: kd> g Breakpoint 3 hit nt!CcGetVacbMiss: 80a1a19e 6a30 push 30h 1: kd> kc # 00 nt!CcGetVacbMiss 01 nt!CcGetVirtualAddress 02 nt!CcMapData 03 Ntfs!NtfsMapStream 04 Ntfs!NtfsReadBootSector Ntfs…

Linux10正式版发布,拥抱AI了!

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 作者&#xff1a;IT邦德 中国DBA联盟(ACDU)成员&#xff0c;10余年DBA工作经验 Oracle、PostgreSQL ACE CSDN博客专家及B站知名UP主&#xff0c;全网粉丝10万 擅长主流Oracle、MySQL、PG、高斯…

关于 SSE(Server-Sent Events)过程的简要解剖

Js前端&#xff1a;发送普通请求 fetch(...) .then(()>{}) .catch(()>{})Java后端&#xff1a;接收请求后调用请求处理函数&#xff0c;函数返回一个emiiter对象 public SseEmitter handleRequest(...) {// 创建一个 SseEmitter 对象&#xff0c;用于发送 SSE 事件SseE…

PyTorch 中unsqueeze(-1)用法

unsqueeze(-1) 是 PyTorch 中的一个张量操作&#xff0c;用于‌在指定维度上增加一个长度为1的维度‌&#xff08;即扩展维度&#xff09;。具体解析如下&#xff1a; 功能说明 ‌作用位置‌ -1 表示在张量的‌最后一个维度‌后添加新维度。 &#xff08;等价于 dimlen(tensor.…

RTC技术

什么是RTC RTC&#xff08;Real time communication&#xff09;实时通信&#xff0c;是实时音视频的一个简称&#xff0c;我们常说的RTC技术一般指的是WebRTC技术&#xff0c;已经被 W3C 和 IETF 发布为正式标准。由于几乎所有主流浏览器都支持 WebRTC 标准 API &#xff0c;…

vue+cesium示例:3Dtiles三维模型高度调整(附源码下载)

接到一位知识星友的邀请&#xff0c;实现他需要3Dtiles三维模型的简单高度调整需求&#xff0c;适合学习Cesium与前端框架结合开发3D可视化项目。 demo源码运行环境以及配置 运行环境&#xff1a;依赖Node安装环境&#xff0c;demo本地Node版本:推荐v18。 运行工具&#xff1a;…

详解3DGS

4 可微分的3D高斯 splatting 核心目标与表示选择 我们的目标是从无法线的稀疏SfM点出发&#xff0c;优化出一种能够实现高质量新视角合成的场景表示。为此&#xff0c;我们选择3D高斯作为基本图元&#xff0c;它兼具可微分的体表示特性和非结构化的显式表示优势&#xff0c;既…

构建版本没mac上传APP方法

在苹果开发者的app store connect上架Ios应用的时候&#xff0c;发现需要使用xode等软件来上传iOS的APP。 但是不管是xcode也好&#xff0c;transporter也好&#xff0c;还是命令行工具也好&#xff0c;都必须安装在mac电脑才能使用&#xff0c;。 假如没有mac电脑&#xff0…

Gitee PPM:智能化项目管理如何重塑软件工厂的未来格局

在数字化转型浪潮席卷全球的当下&#xff0c;软件开发行业正经历着前所未有的变革。随着企业项目复杂度呈指数级增长&#xff0c;传统项目管理方式已难以应对多项目并行、跨团队协作等挑战。Gitee项目组合管理&#xff08;PPM&#xff09;作为新一代智能化项目管理解决方案&…

node入门:安装和npm使用

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、安装npm命令nvm 前言 因为学习vue接触的&#xff0c;一直以为node是和vue绑定的&#xff0c;还以为vue跑起来必须要node&#xff0c;后续发现并不是。 看…

单例模式,饿汉式,懒汉式,在java和spring中的体现

目录 饿汉式单例模式 懒汉式单例模式 Spring中的单例模式 关键差异对比 在Java和Spring中的应用场景 手写案例 单例模式是一种创建型设计模式&#xff0c;其核心在于确保一个类仅有一个实例&#xff0c;并提供一个全局访问点来获取该实例。下面将详细介绍饿汉式和懒汉式…

网络编程——UDP网络编程

文章目录 1、sendto()&#xff0c;recvfrom() 与TCP编程不同的是&#xff1a; 无需建立连接&#xff0c;在recvfrom()阻塞等待客户端的数据&#xff0c;收到数据后进入do something进行数据的处理。 1、sendto()&#xff0c;recvfrom() ssize_t sendto(int socket, void *mes…

OpenSSL详解

这里写目录标题 选项&#xff1a;**通用选项&#xff1a;**1. genrsa&#xff1a;生成RSA密钥对3. req&#xff1a;生成证书签名请求4. x509&#xff1a;生成自签名证书 **证书管理&#xff1a;**1. verify&#xff1a;验证证书2. x509&#xff1a;查看证书详情3. crl&#xff…

MySQL的日志和备份

目录 一. MySQL的日志 1.1 日志的作用 1.2 日志的分类 1.3 事务日志 1.4 错误日志 1.5 通用日志 1.6 慢查询日志 1.7 二进制备份 二. 备份 2.1 数据备份的重要性 2.2 备份的分类 2.3 MySQL备份的内容 2.4 备份的注意点 2.5 备份的工具 2.6 实战案例 2.7 mysql…

前端性能优化:如何让网页加载更快?

摘要 想象一下&#xff0c;满心期待点开一个网页&#xff0c;却等了十几秒还卡在加载界面&#xff0c;你是不是瞬间就想关掉走人&#xff1f;这可不是个别用户的 “急性子”&#xff0c;数据显示&#xff0c;网页每多延迟 1 秒&#xff0c;用户流失率可能增加 11%&#xff01;…

[论文阅读]Prompt Injection attack against LLM-integrated Applications

Prompt Injection attack against LLM-integrated Applications [2306.05499] Prompt Injection attack against LLM-integrated Applications 传统提示注入攻击效果差&#xff0c;主要原因在于&#xff1a; 不同的应用对待用户的输入内容不同&#xff0c;有的将其视为问题&a…

微信小程序进阶第2篇__事件类型_冒泡_非冒泡

在小程序中&#xff0c; 事件分为两种类型&#xff1a; 冒泡事件&#xff0c; 当一个组件上的事件被触发后&#xff0c;该事件会向父节点传递非冒泡事件&#xff0c; 当一个组件上的事件被触发后&#xff0c; 该事件不会向父节点传递。 一 冒泡事件 tap&#xff0c; touchst…