《Java面试85题图解版(二)》进阶深化中篇:Spring核心 + 数据库进阶
📘 《Java面试85题图解版(二)》进阶深化中篇:Spring核心 + 数据库进阶
阅读提示:这是“图解+比喻+一句话总结”面试题库第二篇的进阶深化中篇,覆盖Spring核心与Spring Boot(9题)和数据库进阶(8题)共17道高频面试题。每道题仍然是四层结构——结构图 → 场景比喻 → 关键对比表 → 一句话总结。
三、Spring 核心与 Spring Boot(第44-52题)
📌 第44题:Spring IoC 与 DI
一图看清
传统方式(控制正转) IoC方式(控制反转) 应用程序主动 new 对象 容器创建对象并注入 "我去拿" "我给你"比喻记忆:DIY组装 vs 品牌整机
- 传统:自己跑电子城买CPU、主板、内存,自己组装。得知道每样配件的型号、接口。
- IoC:直接买品牌机,描述需求“我要打游戏”。工厂帮你采购所有零件,组装好,插上电,送到桌上。
- DI:电脑送到时显卡、内存已插好,系统已装好——你关心整体功能,不关心零件间怎么连线。
💡 一句话总结:IoC是把控制权交给容器,DI是容器把装配好的对象推给你。
📌 第45题:Spring Bean 生命周期
一图看清
实例化 → 属性注入(@Autowired) → 各种Aware接口回调 → BeanPostProcessor前置处理 → @PostConstruct初始化方法 → InitializingBean.afterPropertiesSet() → 自定义init-method → BeanPostProcessor后置处理(AOP代理在此发生) → 使用 → @PreDestroy → DisposableBean.destroy() → 自定义destroy-method比喻记忆:新员工入职到离职
- 实例化:签劳动合同。
- 属性注入:配发电脑、工牌(依赖注入)。
- Aware回调:知道自己工号(BeanNameAware)、所属部门(BeanFactoryAware)。
- @PostConstruct:安好自己工位,开始工作。
- AOP代理:给他配个助理(代理对象),对外由助理处理。
- @PreDestroy:离职前交接工作。
💡 一句话总结:Spring Bean从实例化到销毁经历十几步,AOP代理在后置处理器环节生成。
📌 第46题:Spring AOP 底层原理
一图看清
JDK动态代理:基于接口,Proxy.newProxyInstance生成代理类 CGLIB代理:基于继承,生成目标类的子类 Spring Boot 1.x:有接口用JDK,无接口用CGLIB Spring Boot 2.x起:默认CGLIB(即使有接口也用它)比喻记忆:两种明星经纪模式
- JDK代理:明星只按合同走穴(接口),经纪人代理合同上的所有服务。
- CGLIB代理:经纪人直接扮演明星的替身(继承明星类),连合同上没写的行程都接管了。
💡 一句话总结:有接口用JDK,无接口用CGLIB;Boot 2.x起默认CGLIB,final类无法代理。
📌 第47题:Spring 循环依赖与三级缓存
一图看清
三级缓存: 一级 singletonObjects:完全初始化好的Bean(成品) 二级 earlySingletonObjects:早期半成品引用 三级 singletonFactories:对象工厂,可生成代理 A实例化 → 三级缓存存A工厂 → 填充B属性,发现需要B → B实例化 → 三级缓存存B工厂 → 填充A属性,从三级获取A早期引用 → B完成 → 放入一级缓存 → A获得完整B → A完成 → 一级缓存比喻记忆:情侣杯制作
- 制作A杯:捏出A杯泥胚,模具放入三级。要在A上印B头像,B还没做。
- 制作B杯:捏B杯,印A头像时从三级取出A的模具。B烧制完成,放入成品柜(一级)。
- 回到A杯:印上已完成B的头像,A烧制完成。
⚠️ 构造器注入无法解决:杯胚还没捏出来就要对方头像,不可能。
💡 一句话总结:三级缓存解单例Setter循环依赖,构造器注入无解。
📌 第48题:Spring Boot 自动装配原理
一图看清
@SpringBootApplication → @EnableAutoConfiguration → @Import(AutoConfigurationImportSelector) → 读取 META-INF/spring/...AutoConfiguration.imports → 加载所有候选配置类 → @Conditional条件过滤(@ConditionalOnClass、@ConditionalOnMissingBean等) → 条件满足则创建Bean比喻记忆:商场智能供货系统
- AutoConfiguration.imports:全球所有能供货的品牌清单。
- AutoConfigurationImportSelector:供应链经理,挨个打电话看品牌是否还活着(classpath有无)。
- @ConditionalOnMissingBean:你已经自己进了耐克鞋?那我就不供了,避免摆两家打架。
- 最终效果:空货架自动摆上默认商品,开箱即用。你自己进的货(自定义Bean)优先。
💡 一句话总结:读全量候选配置→条件过滤→只装你缺的,用户自定义覆盖默认。
📌 第49题:@SpringBootApplication 三个核心注解
一图看清
@SpringBootApplication = @SpringBootConfiguration(本质是@Configuration,标记配置类) + @EnableAutoConfiguration(自动装配) + @ComponentScan(组件扫描,默认扫描主类所在包及子包)比喻记忆:三合一办公套件
- @SpringBootConfiguration:挂牌“此房间是配置办公室”。
- @EnableAutoConfiguration:自动雇齐前台、保洁、网管。
- @ComponentScan:扫描全公司,看谁挂着工牌(@Component、@Service、@Controller等)。
💡 一句话总结:一个注解三功能:配置类+自动装配+组件扫描。
📌 第50题:@Transactional 失效场景
一图看清
生效前提:public方法 + 经过Spring代理调用 + 异常正确抛出 常见失效: 同类内部调用(this.method()不走代理) 方法非public(private/protected) 异常被catch吞掉没抛出去 rollbackFor未指定,只回滚RuntimeException和Error,受检异常不回滚 数据库引擎不支持事务(MyISAM) 多线程拿不到同一个事务比喻记忆:酒店无线门卡
- 走过代理:必须刷门卡过闸机(AOP代理),系统才记录进出。
- 同类调用:在房间里直接把东西递给朋友,不经过闸机,酒店系统不知道。
- catch吞异常:前台手动掩盖了出错的记录,财务部不知道自然不会退款。
- 受检异常:酒店只在“房费付不了”时自动退款(RuntimeException)。忘带身份证这种小问题不管,除非提前说“任何问题都退款”(rollbackFor = Exception.class)。
💡 一句话总结:事务生效三要素:public方法、走过代理、异常正确抛出。
📌 第51题:Spring MVC 请求处理流程
一图看清
请求 → DispatcherServlet(中央调度) → HandlerMapping(找对应的Controller) → HandlerAdapter(执行Handler) → Controller执行业务 → 返回ModelAndView 或 @ResponseBody → ViewResolver解析视图 / HttpMessageConverter写JSON → 响应返回比喻记忆:电影院放映流程
- DispatcherServlet:售票大厅前台,所有观众先到前台。
- HandlerMapping:排片表,查“《流浪地球》在几号厅”。
- HandlerAdapter:放映员,按下播放键(调用Controller方法)。
- Controller:电影正片(执行业务逻辑)。
- ViewResolver:画面投射到正确银幕,或给手机用户直接发链接(@ResponseBody返回JSON)。
- 拦截器:检票员,入场前查票,散场后检查卫生。
💡 一句话总结:请求经DispatcherServlet统一调度,Mapping找Handler,Adapter执行,ViewResolver渲染。
📌 第52题:Spring Boot Starter 自定义
一图看清
1. 创建autoconfigure模块 → 写自动配置类 + @Conditional条件 2. 在META-INF/spring/...AutoConfiguration.imports声明配置类全限定名 3. 创建starter模块 → pom引入autoconfigure和相关三方依赖 4. 可通过@EnableConfigurationProperties绑定配置属性比喻记忆:乐高套装
- Autoconfigure模块:拼装说明书,告诉你一套零件怎么拼成功能。
- Starter模块:乐高套装盒子,包含说明书+全部零件。
- 你只要引入一个Starter依赖,开箱就能拼出功能模块(如数据源、Redis连接)。
💡 一句话总结:Starter = 自动配置类 + 依赖打包,实现即插即用。
四、数据库进阶(第53-60题)
📌 第53题:MySQL 三日志体系
一图看清
redo log(InnoDB引擎层):物理日志,记录页修改,崩溃恢复,循环写 undo log(InnoDB引擎层):逻辑日志,记录修改前版本,事务回滚+MVCC binlog(Server层):逻辑日志,记录SQL语句变更,主从复制+数据恢复 两阶段提交: 1. redo log prepare → 2. 写binlog → 3. redo log commit 保证redo和binlog一致性比喻记忆:财务三重备份
- redo log:便签纸,改账本前先记“将第3页第5行改成100”。停电后按便签重做。
- undo log:改前复印旧页,老板说撤销就拿复印件还原。别人读旧版也看复印件(MVCC)。
- binlog:审计邮件,每次改完发一封标准格式邮件给审计部(从库),永久存档。
两阶段提交:财务写完便签不擦(prepare),发完审计邮件(写binlog),才在便签打勾(commit)。
💡 一句话总结:redo保崩溃恢复,undo保回滚,binlog保复制,两阶段提交保一致。
📌 第54题:PostgreSQL WAL vs MySQL 日志体系
一图看清
PG WAL:类似 redo log + binlog 综合体 崩溃恢复 ✅ 流复制 ✅ 无独立undo log(旧版本留数据页) MySQL:redo(InnoDB)+ binlog(Server)分开管理 有独立undo log,在回滚段比喻记忆:两种档案管理
- PG WAL:一本总账既记录修改也用于传阅副本(流复制),老版本直接留在账面,定期VACUUM清理。
- MySQL:便签(redo)做恢复 + 审计邮件(binlog)做复制,老版本留底单(undo),purge线程清理。
💡 一句话总结:PG用WAL一统恢复和复制,MySQL用redo+binlog分工协作。
📌 第55题:MySQL 索引失效八大场景
一图看清
失效场景: LIKE '%xxx'(左模糊) WHERE YEAR(time)=2025(函数包裹列) WHERE phone=1380000 但phone是varchar(隐式类型转换) WHERE b=2 联合索引(a,b)跳过最左列 WHERE a!=1 或 NOT IN(负向查询) WHERE a=1 OR b=2(OR部分无索引) 数据量小,全表扫描更快 统计信息不准,优化器误判比喻记忆:字典查字
- 左模糊:找所有含“明”字的词,拼音排序是按开头字母排的,不知道开头只能一页页翻。
- 函数包裹:找“笔画数是12的字”,字典按拼音排不是按笔画,得先数完所有字再找。
- 隐式类型转换:你给数字10查“笔画等于10”,但索引是按文字类别分的,倒过来全错。
- 违背最左前缀:联合索引像“北京→海淀→中关村”地址,跳过北京直接查“海淀”无法快速定位。
💡 一句话总结:索引怕左模糊、函数套列、类型转换、跳过最左列、负向和OR没索引。
📌 第56题:PostgreSQL 高级索引类型
一图看清
B+Tree:通用等值/范围查询 GIN:倒排索引,JSONB包含、数组包含、全文检索 GiST:通用可扩展,空间几何、范围类型、全文 BRIN:块范围索引,极小体积,适合数十亿行的顺序大表 SP-GiST:空间分区,如IP地址比喻记忆:仓库四种找货方式
- B+Tree:常规货架,按编号找,范围查询顺着走。
- GIN:属性倒排本,“红色M码A字裙”查三个清单取交集。
- GiST:仓库平面图,画圆规量距离,测几何形状。
- BRIN:集装箱范围标签,每箱只写“这箱是1月1号到3号的货”。找1月5号的跳过前面20个箱。
💡 一句话总结:B+Tree普适,GIN善包含查询,GiST善空间,BRIN极小体积处理超大表。
📌 第57题:MySQL 主从复制 vs PG 流复制
一图看清
MySQL:主写binlog → 从I/O线程拉取→写入relay log → SQL线程重放 PG:主WAL流持续发送 → 备库实时重放WAL,物理同步 PG10+逻辑复制:发布/订阅模型,表级复制,类似MySQL binlog比喻记忆:两种远程教学
- MySQL:老师讲课录像(binlog)发给分校,分校老师看录像回放(重放)。
- PG流复制:实时直播(WAL流),分校同步播放。
- PG逻辑复制:只转发指定科目的课程录像。
💡 一句话总结:MySQL基于binlog逻辑复制,PG基于WAL物理流复制,PG10+亦支持逻辑复制。
📌 第58题:doublewrite vs full_page_writes
一图看清
部分写失效:16KB页写到一半断电,页损坏无法恢复 MySQL doublewrite:脏页先顺序写共享表空间的doublewrite buffer,再写数据文件 PG full_page_writes:checkpoint后首次修改,全页镜像写入WAL 目的相同:防止页断裂比喻记忆:快递防碎包装
- MySQL doublewrite:先给完整打包好的杯子拍照存保险柜(doublewrite buffer)。运输碎了就拿照片恢复。
- PG full_page_writes:不在别处存备份,但在发货清单(WAL)上画了杯子完整包装图。碎了查清单找回。
💡 一句话总结:两者都防页断裂,MySQL额外存副本,PG把完整页嵌入WAL。
📌 第59题:MySQL vs PG 并发控制差异
一图看清
MySQL InnoDB:锁索引记录,Record Lock + Gap Lock + Next-Key Lock防幻读 PG:元组标记xmin/xmax,更新不阻塞读,锁信息存在tuple中,无专门锁内存区比喻记忆:两种餐厅管理
- MySQL:预约制,订位就锁住餐桌,别人不能坐(主动加锁)。
- PG:自助模式,坐下后在桌上放“已占”牌(标记元组)。别人能看到空余座位但不能占你的。你走了得清理牌子(VACUUM)。
💡 一句话总结:MySQL主动加锁防冲突,PG被动标记靠清理。
📌 第60题:MySQL分库分表 vs PG原生分区
一图看清
MySQL:依赖中间件(ShardingSphere, MyCat),应用层处理路由 PG:原生声明式分区(范围/列表/哈希),自动路由,分区裁剪比喻记忆:手动分柜 vs 智能仓储
- MySQL中间件:几个独立小仓库,你拿单子自己判断去哪个仓库找。
- PG原生分区:一个智能大仓库,系统按标签自动把货分到对应区域。找货时告诉你哪些区域不用看(分区裁剪)。
💡 一句话总结:MySQL靠中间件手工分片,PG原生分区透明路由+自动裁剪。
📌 下篇预告
本篇(中篇)我们深入了Spring核心(IoC、AOP、循环依赖、事务失效等)和数据库进阶(MySQL与PG日志、索引、复制、分片等),共17道面试必考题。
下一篇(进阶深化下篇)将专注于Redis缓存,涵盖8道高频题:为什么快、数据类型、持久化、过期淘汰、穿透击穿雪崩、分布式锁、哨兵与集群、6.0多线程模型。
👉 点击关注我,进阶深化下篇更新后第一时间推送!
