【大白话说Java面试题 第159题】【06_Spring篇】第19题: Spring事务底层原理
PDF大白话说Java面试题 — 06_Spring篇第19题Spring 事务的底层原理回答核心考点Spring 事务的底层原理远不止AOP 动态代理这么简单。大厂面试中面试官期望你深入理解声明式事务 vs 编程式事务的选型差异、EnableTransactionManagement 的启动链路AutoProxyRegistrar ProxyTransactionManagementConfiguration、TransactionInterceptor 的完整拦截流程createTransactionIfNecessary → proceedWithInvocation → commit/rollback、七种传播行为的底层实现ThreadLocal 绑定 ConnectionHolder、回滚规则的精确语义默认仅回滚 RuntimeException/Errorchecked 异常需显式配置以及事务失效的 10 大经典场景。面试官真正想判断的是你是否能从源码级理解 Spring 如何将事务逻辑编织到业务方法中以及能否在生产环境中快速定位和解决事务问题。1. Spring 事务的两种实现方式1.1 编程式事务通过TransactionTemplate或PlatformTransactionManager手动控制事务边界灵活性高但代码侵入性强。// 编程式事务示例AutowiredprivateTransactionTemplatetransactionTemplate;publicvoidexecuteWithTx(){transactionTemplate.execute(status-{userDao.insert(user);orderDao.insert(order);returnnull;});}缺点业务代码中掺杂事务管理逻辑可读性差维护成本高。1.2 声明式事务通过Transactional注解 AOP 实现零侵入最细粒度到方法级别。Transactional(rollbackForException.class,propagationPropagation.REQUIRED)publicvoidexecuteWithTx(){userDao.insert(user);orderDao.insert(order);}优点业务代码纯净事务逻辑与业务逻辑解耦。缺点最细粒度只能到方法级别无法像编程式事务那样精确控制代码块级别的事务边界。[citation:0]选型建议绝大多数场景使用声明式事务仅在需要精细控制事务边界如部分提交、嵌套事务复杂交互时使用编程式事务。2. 声明式事务的启动链路------EnableTransactionManagement 做了什么2.1 核心注解解析EnableTransactionManagement通过Import(TransactionManagementConfigurationSelector.class)向 Spring 容器注册两个关键组件[citation:1]组件作用关键代码AutoProxyRegistrar启用 AOP 自动代理功能注册InfrastructureAdvisorAutoProxyCreatorBeanPostProcessor拦截所有 Bean 的创建过程AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry)ProxyTransactionManagementConfiguration注册事务 Advisor、TransactionAttributeSource、TransactionInterceptorBeanFactoryTransactionAttributeSourceAdvisorTransactionInterceptor2.2 代理创建规则InfrastructureAdvisorAutoProxyCreator在 Bean 创建后判断是否需要创建代理从当前类向上遍历父类、接口、父接口检查是否有Transactional注解检查类的任意 public 方法上是否有Transactional注解若满足条件则通过DefaultAopProxyFactory创建代理实现接口用 JDK 动态代理否则用 CGLIB。[citation:0]// DefaultAopProxyFactory 源码publicAopProxycreateAopProxy(AdvisedSupportconfig){if(config.isOptimize()||config.isProxyTargetClass()||hasNoUserSuppliedProxyInterfaces(config)){Class?targetClassconfig.getTargetClass();if(targetClass.isInterface()||Proxy.isProxyClass(targetClass)){returnnewJdkDynamicAopProxy(config);// JDK 动态代理}returnnewObjenesisCglibAopProxy(config);// CGLIB 代理}returnnewJdkDynamicAopProxy(config);}3. TransactionInterceptor 的完整拦截流程3.1 核心入口invokeWithinTransaction当代理对象的方法被调用时实际执行的是TransactionInterceptor.invoke()其内部调用invokeWithinTransaction()完成事务全生命周期管理[citation:1]protectedObjectinvokeWithinTransaction(Methodmethod,NullableClass?targetClass,finalInvocationCallbackinvocation)throwsThrowable{// 1. 解析 Transactional 注解获取事务属性finalTransactionAttributetxAttrgetTransactionAttributeSource().getTransactionAttribute(method,targetClass);// 2. 确定事务管理器finalTransactionManagertmdetermineTransactionManager(txAttr);PlatformTransactionManagerptmasPlatformTransactionManager(tm);if(txAttrnull||!(ptminstanceofCallbackPreferringPlatformTransactionManager)){// 3. 创建事务或加入已有事务TransactionInfotxInfocreateTransactionIfNecessary(ptm,txAttr,joinpointIdentification);ObjectretVal;try{// 4. 执行业务方法retValinvocation.proceedWithInvocation();}catch(Throwableex){// 5. 异常处理根据回滚规则决定提交或回滚completeTransactionAfterThrowing(txInfo,ex);throwex;}finally{// 6. 清理事务信息从 ThreadLocal 中移除cleanupTransactionInfo(txInfo);}// 7. 正常完成提交事务commitTransactionAfterReturning(txInfo);returnretVal;}}3.2 事务开启createTransactionIfNecessary内部调用getTransaction()根据传播行为决定是创建新事务还是加入已有事务从DataSourceUtils.getConnection(dataSource)获取数据库连接设置autoCommit false这是 Spring 事务的核心禁止自动提交[citation:2]将连接绑定到当前线程的ThreadLocalConnectionHolder中保证同一事务内多个 DAO 操作共享同一连接。3.3 事务提交与回滚场景行为源码方法方法正常返回提交事务commitTransactionAfterReturning()抛出 RuntimeException / Error回滚事务completeTransactionAfterThrowing()抛出 checked Exception默认提交事务completeTransactionAfterThrowing()异常被 try-catch 未抛出提交事务异常未传播到拦截器关键陷阱默认情况下只有RuntimeException 和 Error会触发回滚checked Exception如IOException、SQLException不会回滚必须通过rollbackFor Exception.class显式配置。[citation:3]4. 七种事务传播行为深度解析Spring 事务传播机制通过ThreadLocal实现本质上是维护当前线程的事务上下文TransactionSynchronizationManager中的resourcesThreadLocalMap。[citation:4]传播行为含义底层实现适用场景REQUIRED默认当前有事务则加入无则新建检查 ThreadLocal 中是否存在连接存在则复用绝大多数业务方法REQUIRES_NEW挂起当前事务新建独立事务将当前连接解绑到suspendedResources创建新连接绑定日志记录、审计等独立操作NESTED在当前事务中创建保存点Savepoint利用 JDBC Savepoint 机制部分回滚场景SUPPORTS有事务则加入无则以非事务执行检查 ThreadLocal无则直接执行查询方法NOT_SUPPORTED挂起当前事务以非事务执行解绑当前连接执行完再恢复不需要事务的批量操作MANDATORY必须有事务否则抛异常检查 ThreadLocal无则抛IllegalTransactionStateException强制事务上下文的方法NEVER必须无事务否则抛异常检查 ThreadLocal有则抛异常纯查询方法禁止事务REQUIRED vs REQUIRES_NEW 的核心差异// 场景methodA (REQUIRED) 调用 methodB (REQUIRES_NEW)TransactionalpublicvoidmethodA(){dao.insertA();// 在事务 T1 中serviceB.methodB();// 挂起 T1开启 T2thrownewRuntimeException();// T1 回滚但 T2 已独立提交}Transactional(propagationPropagation.REQUIRES_NEW)publicvoidmethodB(){dao.insertB();// 在独立事务 T2 中}methodB的事务 T2 与methodA的事务 T1 完全独立T1 回滚不影响 T2。这是日志记录、审计等必须成功操作的标准用法。[citation:5]5. 事务失效的 10 大经典场景失效场景原因解决方案1. 方法非 publicTransactional只能作用于 public 方法非 public 方法代理不拦截改为 public或改用编程式事务2. 同类方法自调用this.method()不走代理直接调用目标对象注入自身代理或改用AopContext.currentProxy()[citation:0]3. 异常被 try-catch 吞掉异常未传播到 TransactionInterceptor拦截器感知不到异常catch 后重新抛出或手动回滚TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()4. 抛出 checked Exception默认不回滚 checked 异常配置rollbackFor Exception.class[citation:3]5. 错误配置 noRollbackFor显式排除了本应回滚的异常检查noRollbackFor配置6. 数据库引擎不支持事务MyISAM 引擎不支持事务改用 InnoDB7. 传播行为配置错误如NOT_SUPPORTED导致方法非事务执行根据业务语义选择正确的传播行为8. 异步方法AsyncAsync会在新线程执行ThreadLocal 事务上下文丢失异步方法内部重新开启事务或改用分布式事务9. 多数据源未指定事务管理器Transactional未指定transactionManager使用默认的可能不匹配显式指定transactionManager xxxManager10. 事务方法被 final/static 修饰CGLIB 代理通过继承实现final/static 方法无法被代理覆盖去掉 final/static 修饰同类自调用陷阱详解ServicepublicclassOrderService{// ❌ 错误methodA 调用 methodB 不走代理事务失效publicvoidmethodA(){this.methodB();// 直接调用不经过 TransactionInterceptor}TransactionalpublicvoidmethodB(){orderDao.insert(order);}}// ✅ 正确注入自身代理对象ServicepublicclassOrderService{AutowiredprivateOrderServiceself;// 注入代理对象publicvoidmethodA(){self.methodB();// 通过代理调用事务生效}TransactionalpublicvoidmethodB(){orderDao.insert(order);}}6. 核心组件深度对比组件接口/类职责典型实现事务管理器PlatformTransactionManager定义事务的开启、提交、回滚标准接口DataSourceTransactionManagerJDBC、JpaTransactionManagerJPA事务定义TransactionDefinition封装隔离级别、传播行为、超时、只读等属性DefaultTransactionDefinition事务状态TransactionStatus记录事务当前状态是否新事务、是否回滚标记DefaultTransactionStatus事务属性源TransactionAttributeSource解析Transactional注解获取事务属性AnnotationTransactionAttributeSource事务拦截器TransactionInterceptorAOP 拦截器编织事务逻辑到目标方法TransactionInterceptor事务同步管理器TransactionSynchronizationManager通过 ThreadLocal 管理当前线程的事务资源TransactionSynchronizationManager7. 面试官追问与高分回答模板追问 1“Spring 事务的底层原理是什么”低分回答“基于 AOP 和动态代理通过 TransactionInterceptor 拦截方法。”太浅没有触及启动链路和传播机制高分回答 “Spring 声明式事务的底层是一个完整的 AOP 链路 1.启动阶段EnableTransactionManagement通过Import注册AutoProxyRegistrar启用 AOP 自动代理和ProxyTransactionManagementConfiguration注册TransactionInterceptor。 2.代理阶段InfrastructureAdvisorAutoProxyCreator判断 Bean 是否含Transactional有则创建 JDK 或 CGLIB 代理。 3.运行时阶段方法调用被TransactionInterceptor.invoke()拦截内部调用invokeWithinTransaction()先通过TransactionAttributeSource解析注解属性再调用PlatformTransactionManager.getTransaction()根据传播行为获取/创建事务核心是将数据库连接绑定到 ThreadLocal然后执行业务方法最后根据异常类型决定提交或回滚。 4.资源清理cleanupTransactionInfo()从 ThreadLocal 中移除事务信息防止内存泄漏。”追问 2“Transactional 默认在什么异常下回滚checked 异常为什么不回滚”低分回答“默认回滚 RuntimeException。”没有解释设计原因高分回答 “默认只回滚RuntimeException和Error不回滚 checked Exception。这是 Spring 的设计选择 -RuntimeException代表编程错误或不可恢复的系统异常通常需要回滚 - checked Exception 代表业务异常如FileNotFoundException业务层可能已做补偿处理强制回滚可能破坏业务逻辑。 如果业务需要 checked 异常也回滚必须显式配置rollbackFor Exception.class。同时注意如果异常在方法内被 try-catch 吞掉未抛出拦截器感知不到异常事务会正常提交。”追问 3“REQUIRED 和 REQUIRES_NEW 有什么区别什么场景用 REQUIRES_NEW”高分回答 “核心区别在于事务的独立性 -REQUIRED当前有事务则加入无则新建。子方法与父方法共享同一数据库连接同生共死。 -REQUIRES_NEW挂起当前事务新建完全独立的事务。子方法有自己的连接和提交/回滚逻辑父事务回滚不影响子事务。 底层实现上REQUIRES_NEW会将当前线程 ThreadLocal 中的连接解绑到suspendedResources创建新连接绑定到 ThreadLocal子方法执行完后再恢复父事务的连接。 典型场景日志记录、审计操作。例如订单下单失败回滚时操作日志必须保留此时日志方法配置REQUIRES_NEW确保独立提交。”追问 4“同类方法自调用为什么会导致事务失效怎么解决”高分回答 “根本原因是Spring AOP 基于代理实现。当methodA()内部调用this.methodB()时this指向的是目标对象本身而非代理对象。事务逻辑开启、提交、回滚都编织在代理对象中直接调用目标对象绕过了TransactionInterceptor导致事务失效。 解决方案有三种 1.注入自身代理Autowired private OrderService self;然后self.methodB() 2.使用AopContext.currentProxy()((OrderService) AopContext.currentProxy()).methodB()需配置EnableAspectJAutoProxy(exposeProxy true) 3.拆分到另一个 Bean将methodB移到OrderService2中通过注入调用。”追问 5“Spring 事务和数据库事务是什么关系”高分回答 “Spring 事务是对数据库事务的抽象和封装本身不实现事务最终依赖数据库的事务机制如 MySQL 的 redo log/undo log、InnoDB 的 MVCC。 Spring 的核心贡献是 1.统一抽象通过PlatformTransactionManager屏蔽 JDBC、JPA、Hibernate 等不同数据访问技术的事务差异 2.声明式管理通过 AOP 将事务逻辑从业务代码中剥离 3.传播机制通过 ThreadLocal 管理连接实现跨方法的事务传播这是 Spring 特有的数据库本身没有传播行为概念。 没有数据库的事务支持Spring 无法提供事务功能。”追问 6“如果事务方法里调用了异步方法Async事务还能生效吗”高分回答 “不能生效。Async会在新线程中执行方法而 Spring 事务通过ThreadLocal绑定连接新线程没有父线程的事务上下文导致事务传播失效。 解决方案 1. 在异步方法内部也加上Transactional让它在自己的事务中执行 2. 如果要求异步方法参与父事务必须使用分布式事务框架如 Seata 3. 如果异步操作必须随父事务回滚考虑改为同步执行。”8. 方案选型速查表业务场景推荐方案核心理由普通 CRUD 业务方法Transactional默认 REQUIRED简单、无侵入、满足绝大多数场景日志/审计必须成功Transactional(propagation REQUIRES_NEW)独立于主事务主事务回滚不影响批量导入部分失败容忍Transactional(propagation NESTED)利用 Savepoint 实现部分回滚纯查询方法Transactional(readOnly true, propagation SUPPORTS)只读优化无事务时也不强制开启不需要事务的批量操作propagation NOT_SUPPORTED挂起事务避免不必要的事务开销强制要求事务上下文propagation MANDATORY调用方必须已开启事务否则抛异常精细控制事务边界TransactionTemplate编程式事务代码块级别控制灵活但侵入性强多数据源事务JtaTransactionManager XA分布式事务两阶段提交面试官想要的满分总结Spring 声明式事务的本质是AOP 代理 ThreadLocal 资源绑定的精密工程。核心链路是EnableTransactionManagement启动 AOP 代理 →TransactionInterceptor拦截方法 →PlatformTransactionManager根据传播行为获取/创建事务 → 将连接绑定到 ThreadLocal → 执行业务方法 → 根据异常规则提交或回滚 → 清理 ThreadLocal。理解 Spring 事务必须抓住三个关键点代理是前提只有经过代理对象调用的方法才会被TransactionInterceptor拦截同类自调用、非 public 方法都会导致事务失效传播行为是核心七种传播行为通过 ThreadLocal 中连接的挂起/恢复/复用来实现REQUIRES_NEW的独立性、NESTED的 Savepoint 机制是高频考点回滚规则是陷阱默认仅回滚RuntimeException/Errorchecked 异常和 try-catch 吞异常是生产环境最常见的两个坑。最后记住Spring 事务是对数据库事务的抽象不是替代。没有数据库的事务支持如 MyISAM 引擎Spring 事务完全无效。工程实践中遇到事务问题首先检查代理是否生效同类自调用其次检查异常是否传播到拦截器被 catch 了最后检查传播行为和回滚规则配置是否正确。觉得对您有帮助麻烦点点关注啦您的关注是我创作的最大动力~