Java技术面试高频题整理报告
📚 今日知识点:Spring事务底层原理与高频考点
一、知识点核心概述
Spring事务是Java后端开发中最核心的面试考点之一,也是生产环境中最容易出问题的模块。掌握Spring事务不仅需要理解"AOP+动态代理"的表层概念,更需要深入理解声明式事务与编程式事务的选型差异、事务传播机制的底层实现、以及事务失效的十大经典场景 。
二、核心概念深度解析
- Spring事务的两种实现方式
| 实现方式 | 实现原理 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| 编程式事务 | 通过TransactionTemplate或PlatformTransactionManager手动控制事务边界 | 灵活性高,可精确控制代码块级别的事务边界 | 代码侵入性强,业务代码中掺杂事务管理逻辑,可读性差 | 需要精细控制事务边界的特殊场景 |
| 声明式事务 | 通过@Transactional注解+AOP实现,零侵入 | 业务代码纯净,事务逻辑与业务逻辑解耦 | 最细粒度只能到方法级别 | 绝大多数业务场景(推荐首选) |
编程式事务代码示例:
java
@Autowired
private TransactionTemplate transactionTemplate;
public void executeWithTx() {
transactionTemplate.execute(status -> {
userDao.insert(user);
orderDao.insert(order);
return null;
});
}
声明式事务代码示例:
java
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
public void executeWithTx() {
userDao.insert(user);
orderDao.insert(order);
}
- 声明式事务启动链路
@EnableTransactionManagement注解通过@Import向Spring容器注册两个关键组件 :
- AutoProxyRegistrar:启用AOP自动代理功能,注册InfrastructureAdvisorAutoProxyCreator
- ProxyTransactionManagementConfiguration:注册事务拦截器配置
动态代理选择逻辑:
java
public AopProxy createAopProxy(AdvisedSupport config) {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config); // JDK动态代理
}
return new ObjenesisCglibAopProxy(config); // CGLIB代理
}
return new JdkDynamicAopProxy(config);
}
- 事务传播机制(七大类型)
| 传播行为 | 含义 | 典型场景 |
|---|---|---|
| REQUIRED(默认) | 如果当前存在事务,则加入该事务;否则创建新事务 | 大多数业务场景 |
| SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行 | 查询操作 |
| REQUIRES_NEW | 创建新事务,如果当前存在事务,则把当前事务挂起 | 需要独立事务的场景 |
| NESTED | 如果当前存在事务,则在嵌套事务内执行 | 部分提交场景 |
| MANDATORY | 如果当前存在事务,则加入该事务;否则抛出异常 | 强制要求事务的场景 |
| NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,则把当前事务挂起 | 不需要事务的场景 |
| NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常 | 禁止事务的场景 |
传播机制底层实现: 通过ThreadLocal维护当前线程的事务上下文(TransactionSynchronizationManager中的resources ThreadLocalMap) 。
- 事务回滚规则
关键陷阱: 默认情况下只有RuntimeException和Error会触发回滚,checked Exception(如IOException、SQLException)不会回滚!
| 异常类型 | 默认行为 | 解决方案 |
|---|---|---|
| RuntimeException | 自动回滚 | 无需配置 |
| Error | 自动回滚 | 无需配置 |
| Checked Exception | 不会回滚 | 需显式配置rollbackFor = Exception.class |
三、高频面试题目及答案解析
🔥 题目1:Spring事务的底层原理是什么?
参考答案:
Spring事务基于AOP动态代理实现。当调用带有@Transactional注解的方法时,实际执行的是TransactionInterceptor.invoke(),其内部调用invokeWithinTransaction()完成事务全生命周期管理 :
- 解析注解属性 :通过
TransactionAttributeSource解析@Transactional注解配置 - 获取/创建事务 :调用
PlatformTransactionManager.getTransaction()根据传播行为获取或创建事务(核心是将数据库连接绑定到ThreadLocal) - 执行业务方法 :调用
proceedWithInvocation()执行目标方法 - 提交或回滚 :根据异常类型决定提交(
commitTransactionAfterReturning())或回滚(completeTransactionAfterThrowing())
🔥 题目2:Spring事务失效的常见场景有哪些?
参考答案: 事务失效的七大经典场景 :
- 方法非public修饰 :
@Transactional只能应用于public方法 - 同类内部方法调用:绕过AOP代理,直接调用目标方法
- 异常被try-catch捕获:未抛出异常,事务拦截器无法感知
- 事务传播机制配置错误 :如使用
NOT_SUPPORTED - 多线程环境下事务互不感知 :事务基于
ThreadLocal,多线程无法共享 - 数据库引擎不支持事务:如MySQL的MyISAM引擎
- 只读事务、超时触发事务回滚机制失效
🔥 题目3:JDK动态代理和CGLIB代理有什么区别?
参考答案:
| 对比维度 | JDK动态代理 | CGLIB代理 |
|---|---|---|
| 实现原理 | 基于接口实现 | 基于子类继承实现 |
| 使用限制 | 目标类必须实现接口 | 可代理任意普通类 |
| 性能差异 | 低版本JDK中性能较差 | 低版本JDK中性能更优 |
| Spring默认规则 | SpringBoot 2.0+默认使用CGLIB | 无接口类自动选用CGLIB |
| 局限性 | 无接口无法代理 | 无法代理final、private、static方法 |
🔥 题目4:Spring如何解决循环依赖问题?
参考答案: Spring通过三级缓存机制解决单例Setter/字段注入循环依赖问题 :
- 一级缓存:存放完整初始化完成的单例Bean
- 二级缓存:存放已实例化、未完全初始化的Bean实例
- 三级缓存:存放Bean工厂对象,提前暴露Bean引用
核心原理: 提前暴露未完成初始化的Bean引用,打破循环依赖闭环。注意: 构造器注入循环依赖无法解决,会直接报错 。
四、常见误区与避坑指南
❌ 误区1:认为@Transactional注解可以作用于任何方法
真相: @Transactional只能作用于public方法,非public方法会导致事务失效 。
❌ 误区2:认为所有异常都会触发事务回滚
真相: 默认只有RuntimeException和Error会回滚,checked Exception需要显式配置rollbackFor 。
❌ 误区3:认为同类内部调用也能触发事务
真相: 同类内部方法调用会绕过AOP代理,直接调用目标方法,导致事务失效 。
解决方案: 通过AopContext.currentProxy()获取代理对象,或注入自身Bean调用。
五、生产环境最佳实践
- 优先使用声明式事务 :绝大多数场景使用
@Transactional,代码更简洁 - 显式配置rollbackFor :始终配置
rollbackFor = Exception.class,避免checked异常不回滚 - 避免同类内部调用 :需要事务增强时,通过注入自身Bean或
AopContext调用 - 注意多线程场景 :事务基于
ThreadLocal,多线程环境下需要特殊处理 - 合理选择传播机制:根据业务场景选择合适的传播行为,避免不必要的事务嵌套
六、延伸学习建议
根据Java面试通关系列的20篇核心知识点规划,建议按以下顺序系统学习 :
- Day 1-10(核心基础):Java基础、集合、并发、JVM,重点掌握HashMap源码、synchronized锁升级、GC回收机制
- Day 11-15(主流框架):Spring、SpringBoot、MyBatis,重点掌握Bean生命周期、循环依赖解决、AOP原理
- Day 16-20(中间件与架构):Redis、MySQL、MQ、分布式,重点掌握缓存三兄弟、MySQL索引失效、Seata分布式事务
七、今日总结
Spring事务是Java后端面试的必考核心考点 ,也是生产环境中的高频问题来源。掌握Spring事务需要:
- 理解声明式事务与编程式事务的选型差异
- 深入掌握七大传播机制的底层实现(ThreadLocal绑定)
- 熟记事务失效的十大经典场景
- 明确回滚规则的精确语义(默认仅回滚RuntimeException/Error)
建议结合源码阅读和实际项目演练,真正打通从"面试造火箭"到"工作拧螺丝"的最后一公里 。