📅 Java技术面试高频题整理 - Day 1
今日知识点:Spring IoC与AOP核心机制
Spring IoC(控制反转)与AOP(面向切面编程)是Spring全家桶的核心基石,也是Java后端面试中必考的高频深挖考点。多数开发者仅会使用框架进行CRUD操作,但对底层原理认知浅薄,难以应对中高级面试的源码提问。
一、IoC控制反转核心原理
1.1 核心概念
IoC即控制反转,是Spring的核心设计思想。它将对象创建、依赖管理的控制权从开发者反转给Spring容器,实现组件解耦、统一管理和灵活复用。
| 传统开发方式 | IoC容器方式 |
|---|---|
开发者主动new对象 | 容器自动创建、装配、管理对象 |
| 高耦合、难维护 | 低耦合、易扩展 |
| 依赖关系硬编码 | 依赖关系外部配置 |
1.2 依赖注入三种方式
① 构造器注入(Spring官方推荐)
// 保证对象初始化完成、依赖不可变、避免空指针 public class UserService { private final UserRepository userRepository; public UserService(UserRepository userRepository) { this.userRepository = userRepository; } }② Set方法注入
适合可选依赖、动态修改依赖场景。
③ 字段注入(不推荐生产使用)
写法简洁但耦合度高,不利于单元测试。
1.3 Bean完整生命周期
实例化 → 依赖注入 → 初始化前置处理 → 初始化方法 → 初始化后置处理 → 存入单例池 → 销毁
全程由Spring容器全权管理,是面试高频源码考点。
二、Spring三级缓存解决循环依赖
2.1 循环依赖问题
循环依赖是多Bean互相依赖导致的初始化死循环。Spring通过三级缓存机制完美解决单例Setter/字段注入循环依赖问题。
2.2 三级缓存结构
| 缓存级别 | 存放内容 | 作用 |
|---|---|---|
| 一级缓存 | 完整初始化完成的单例Bean | 正常获取Bean |
| 二级缓存 | 已实例化、未完全初始化的Bean实例 | 提前暴露引用 |
| 三级缓存 | Bean工厂对象 | 解决循环引用 |
2.3 核心原理
提前暴露未完成初始化的Bean引用,打破循环依赖闭环,保证Bean正常创建。
⚠️注意:仅支持单例、非构造器注入循环依赖。构造器注入循环依赖无法解决,会直接报错。
三、AOP面向切面编程
3.1 核心概念
AOP是面向切面编程,在不修改原有代码的前提下对方法进行增强,实现代码解耦、统一增强、复用通用逻辑。
典型应用场景:事务控制、日志记录、权限校验、性能监控。
3.2 两种动态代理对比
| 对比维度 | JDK动态代理 | CGLIB动态代理 |
|---|---|---|
| 实现原理 | 基于接口实现 | 基于子类继承实现 |
| 使用限制 | 目标类必须实现接口 | 可代理任意普通类 |
| 性能差异 | 高版本JDK性能持平 | 低版本JDK更优 |
| Spring默认 | SpringBoot 2.0+默认CGLIB | 无接口类自动选用 |
| 局限性 | 无接口无法代理 | 无法代理final/private/static方法 |
3.3 五种通知类型
- 前置通知:方法执行前
- 后置通知:方法执行后
- 异常通知:方法抛出异常时
- 环绕通知:功能最全,可控制方法执行全程
- 返回通知:方法正常返回后
四、Spring事务核心机制
4.1 事务传播机制
| 传播机制 | 说明 | 适用场景 |
|---|---|---|
| REQUIRED(默认) | 支持当前事务,不存在则新建 | 大多数业务场景 |
| SUPPORTS | 支持当前事务,不存在则以非事务执行 | 查询操作 |
| REQUIRES_NEW | 新建事务,挂起当前事务 | 独立日志记录 |
| NESTED | 嵌套事务 | 复杂业务嵌套 |
4.2 事务隔离级别
读未提交 → 读已提交 → 可重复读 → 串行化,逐级解决脏读、不可重复读、幻读问题。MySQL默认可重复读。
4.3 事务失效七大场景(高频坑点)
- 方法非
public修饰 - 同类内部方法调用,绕过AOP代理
- 异常被
try-catch捕获,未抛出异常 - 事务传播机制配置错误
- 多线程环境下事务互不感知
- 数据库引擎不支持事务(如MyISAM)
- 只读事务、超时触发事务回滚机制失效
五、高频面试真题及标准答案
Q1:IoC的核心作用是什么?
答:IoC将对象创建、依赖管理的控制权从开发者交给Spring容器。核心作用:彻底解耦组件依赖,统一管理Bean生命周期,简化代码开发,提升程序扩展性、维护性与灵活性,是Spring框架的核心根基。
Q2:Spring如何解决循环依赖?
答:Spring通过三级缓存提前暴露Bean引用解决单例Setter循环依赖。流程:Bean实例化后存入三级缓存,提前暴露引用,被依赖Bean直接获取引用完成初始化,后续逐级升级缓存,打破循环依赖闭环。仅支持单例、非构造器注入循环依赖,构造器循环依赖无法解决。
Q3:JDK动态代理和CGLIB动态代理的区别?
答:
- 实现原理:JDK基于接口,CGLIB基于继承子类重写方法
- 使用限制:JDK要求目标类实现接口,CGLIB可代理任意普通类
- 性能差异:低版本JDK中CGLIB更优,高版本二者持平
- Spring默认:SpringBoot 2.0+默认使用CGLIB
- 局限性:CGLIB无法代理final、private、static方法
Q4:Spring事务失效的常见场景有哪些?
答:
- 方法非public修饰
- 同类内部方法调用,绕过AOP代理
- 异常被try-catch捕获
- 事务传播机制配置错误
- 多线程环境下事务互不感知
- 数据库引擎不支持事务
- 只读事务、超时触发回滚失效
六、学习建议与避坑指南
✅ 重点掌握
- 三级缓存解决循环依赖原理
- 两种动态代理区别及适用场景
- 事务传播机制适配场景
- 高频事务失效坑点
❌ 常见误区
- 认为字段注入是最佳实践(实际不推荐)
- 忽视构造器注入循环依赖无法解决
- 事务注解加在private方法上
- 同类方法调用时忽略AOP代理绕过问题
📚 延伸学习
建议结合Spring源码深入理解Bean生命周期、三级缓存实现细节、AOP代理创建过程,彻底掌握Spring框架底层运行逻辑。
明日预告:Java集合框架核心考点(HashMap源码、ConcurrentHashMap并发机制、ArrayList与LinkedList对比)
参考来源
- 适合中高级前端vue面试的100道题目及解析_vue.js_脚本之家
- Java面试通关⑧:Spring核心IoC/AOP全集-CSDN博客
- Java中的final/finally/finalize与对象4种引用全解析_java_脚本之家
- 第7篇:工厂模式面试高频题与避坑指南-CSDN博客
- Java面试宝典:从基础到架构9-CSDN博客
- 【Java实习面试算法冲刺】哈希!-CSDN博客