知其然,知其所以然
Spring 全家桶 2026-10-19

@Retryable:Spring Framework 7 内置的重试、退避与异常筛选(以及 @ConcurrencyLimit)

实测 Spring Framework 7 内置的 @Retryable:默认 maxRetries = 3、delay = 1000 ms(执行时刻 5、1014、2019 ms);multiplier = 2、maxDelay = 500 时间隔 100、200、400、500、500;includes / excludes / predicate 筛选异常(嵌套的 cause 也参与匹配);次数用完抛原始异常,timeout 把 maxRetries = 10 截断成 3 次;RetryTemplate 次数用完抛 RetryException;@ConcurrencyLimit(2) 把最大并发限制成 2。

Spring Retryable 重试 退避 ConcurrencyLimit 真实验证 动画演示
Spring 全家桶 2026-10-19

AopContext 与 AopUtils:拿到原始对象

实测怎么从代理拿回原始对象与类:AopUtils.getTargetClass 只脱一层,AopProxyUtils.ultimateTargetClass 一路脱到底,ClassUtils.getUserClass 不处理 JDK 代理;CGLIB 代理类上 getAnnotation 找不到目标类注解(findAnnotation 才行);代理与目标 equals、hashCode 都不相等;两个切面加 @Async 合并成同一个代理的 4 个拦截器。

Spring AOP 代理 AopUtils AopProxyUtils 真实验证 动画演示
Spring 全家桶 2026-10-19

@Transactional 的实现:TransactionInterceptor 到 PlatformTransactionManager

实测 @Transactional 的实现:事务管理器依次 doGetTransaction、doBegin(拿连接、关 autocommit、绑定线程)、doCommit 或 doRollback、doCleanupAfterCompletion;事务里两条 insert 用同一个连接(connection_id 都是 12);MySQL general_log 里一个事务是 SET autocommit=0 … COMMIT … SET autocommit=1 共 7 条,没有 @Transactional 的两条 insert 只有两条语句本身。

Spring 事务 Transactional PlatformTransactionManager MySQL 真实验证 动画演示
Spring 全家桶 2026-10-19

事务传播行为:7 种逐一实测

实测事务传播行为 7 种:外面有事务时 REQUIRED、NESTED、SUPPORTS、MANDATORY 加入同一个连接,REQUIRES_NEW 与 NOT_SUPPORTED 用另一个连接,NEVER 抛异常;外层回滚时 REQUIRES_NEW 与 NOT_SUPPORTED 的行保留;inner 抛异常被外层吞掉时 REQUIRED 在提交时抛 UnexpectedRollbackException,REQUIRES_NEW 与 NESTED 只回滚自己;外面没有事务时 MANDATORY 抛异常。

Spring 事务 传播行为 REQUIRES_NEW NESTED 真实验证 动画演示
Spring 全家桶 2026-10-19

回滚规则:检查异常、RuntimeException 与 rollbackFor

实测事务回滚规则:RuntimeException 与 Error 回滚,受检异常 IOException 默认提交(表里剩 1 行);rollbackFor 才会回滚;方法内部吞掉异常就提交,setRollbackOnly() 可以手动回滚;rollbackFor 与 noRollbackFor 同时匹配时继承层次上更近的一条赢。

Spring 事务 回滚 rollbackFor 受检异常 真实验证 动画演示
Spring 全家桶 2026-10-19

事务失效的场景:自调用、非 public、异常被吞与多线程

实测 @Transactional 不生效的场景:自调用、private、final、吞掉异常、事务里开线程、事务管理器绑错数据库、MyISAM 表、new 出来的对象都不回滚;Spring 7 的 CGLIB 代理下 package-private 与 protected 的方法是生效的;默认 JDK 代理只能按接口取 Bean。

Spring 事务 失效 自调用 真实验证 动画演示
Spring 全家桶 2026-10-19

readOnly 的真实效果:MySQL 与 Hibernate flush 模式

实测 readOnly = true 的效果:JDBC 一侧靠 SET SESSION TRANSACTION READ ONLY 开始、READ WRITE 结束,里面用 JdbcTemplate 写会被驱动在客户端拒绝;Hibernate 一侧实体修改不再刷到数据库(没有 UPDATE,手动 em.flush() 也不报错,修改悄悄丢掉);加入外层读写事务时内层的 readOnly 无效。

Spring 事务 readOnly Hibernate flush 真实验证 动画演示
Spring 全家桶 2026-10-19

TransactionSynchronizationManager:事务资源绑在线程上

实测事务资源绑定在线程上:事务内线程上绑着 HikariDataSource → ConnectionHolder,子线程里事务不活跃、资源 0 个;同步回调提交时依次是 beforeCommit、beforeCompletion、afterCommit、afterCompletion(COMMITTED),回滚时没有 beforeCommit 和 afterCommit;afterCommit 抛异常撤销不了提交;绕过 Spring 直接 ds.getConnection() 拿到另一个连接,写入不会回滚。

Spring 事务 TransactionSynchronizationManager ThreadLocal 真实验证 动画演示
Spring 全家桶 2026-10-19

TransactionTemplate 与编程式事务

实测 TransactionTemplate:回调抛 RuntimeException 或 setRollbackOnly() 都回滚;在同一个类里使用没有自调用问题;事务超时通过语句超时实现(setTimeout(1) 约 1025 ms 取消 select sleep(3));把 300 ms 的非数据库工作移出事务后,8 线程 2 连接的池从 1225 ms 降到 308 ms。

Spring 事务 TransactionTemplate 编程式事务 连接池 真实验证 动画演示
Spring 全家桶 2026-10-19

嵌套事务与 Savepoint

实测 NESTED 就是 JDBC Savepoint:MySQL 上依次出现 SAVEPOINT、(失败时 ROLLBACK TO SAVEPOINT)、RELEASE SAVEPOINT,整个过程只有一个 COMMIT;每次多出两条语句,耗时是 REQUIRED 的两倍多;JpaTransactionManager 默认不允许 NESTED,打开之后 Hibernate 的 JpaDialect 仍然不支持 savepoint。

Spring 事务 NESTED Savepoint 嵌套事务 真实验证 动画演示
Spring 全家桶 2026-10-19

多数据源事务:各自提交的风险与分布式事务的边界

实测多数据源下一个事务管理器只管一个数据源:@Transactional(txA) 写 A 和 B 后抛异常,A 回滚而 B 留下 1 行;嵌套模板先提交的一方撤销不了,后提交的失败会让两库不一致(七个场景里 4 个不一致);只有恰好是先提交的那一方失败时才碰巧一致。

Spring 事务 多数据源 分布式事务 最终一致 真实验证 动画演示
Spring 全家桶 2026-10-19

事务与 @Async、@TransactionalEventListener:提交之后再做事

实测 @TransactionalEventListener:普通 @EventListener 在事务里立刻执行,AFTER_COMMIT 在提交之后,回滚时收到 AFTER_ROLLBACK;@Transactional(REQUIRED) 的 AFTER_COMMIT 监听器在 Spring 7.0.9 启动时就被拒绝;@Async @EventListener 有 10% 读不到刚提交的订单(90/100),换成 AFTER_COMMIT 是 100/100;AFTER_COMMIT 监听器抛异常只记日志、不传给调用者。

Spring 事务 TransactionalEventListener AFTER_COMMIT 事务事件 真实验证 动画演示
京ICP备2026067502号-1京公网安备11010502063487号