Skip to content

项目 A:复盘与简历表达 ​

S5-007必学进阶约 60 分钟JDK 21项目实战 · 项目 A:测试管理平台

1. 本节定位 ​

项目做完了,最后一步是把它变成你的东西:

text
① 复盘:哪些做得好、哪些踩了坑、下次怎么办 ✓
② 提炼:这个项目最有说服力的技术点是什么 ✓✓
③ 表达:面试官 30 秒内能听懂你做了什么、难在哪 ✓✓✓

★ 一条残酷的事实:做了但讲不出来 = 没做 ✗✗

2. 学习目标 ​

  • 能用固定框架做复盘(而不是"感觉学到了") ★★★;
  • 能从项目里提炼出 3 个技术亮点 ★★★;
  • 能用 STAR 法则写出项目描述 ★★★;
  • 能预判面试追问并准备好证据 ★★★。

3. 前置知识检查 ​

4. 为什么需要这个知识 ​

先看两种简历写法的对比 ✗✓:

text
✗ 弱:「独立开发了一个图书管理系统,使用 Spring Boot + MySQL,
      实现了借书还书等功能,学习了分层架构。」

✓ 强:「设计并实现图书借阅系统(Spring Boot + JdbcTemplate + H2/PostgreSQL),
      30 个测试全部通过;针对并发超借问题,用「条件 UPDATE 原子扣减」替代
      check-then-act,10 线程抢 1 本库存恰好 1 个成功(并发测试验证);
      借还操作全部幂等(条件更新),重复归还库存只加一次。」

★ 差别不在项目,在表达 ✓✓:弱版本任何人都能写,强版本只有做过的人才写得出来 ✓

5. 核心概念 ​

5.1 复盘的固定框架 ★★★ ​

★ 三个问题,每个都要有具体证据 ✓✓:

问题本项目的回答(真实)
做得好的 ★★★并发安全用测试证明(10 抢 1 恰好 1 个成功);规则全部配置化;约束兜底(CHECK 拦住负库存)
踩了坑的 ★★★Mockito 在 JDK 25 跑不了;KeyHolder 多列;测试数据被准备工作清掉(不报错只结果错);@RequestParam 缺 name 全部 500
下次怎么办 ★★★先验证测试工具在当前环境能跑;并发测试绝不用 Mock;测试数据准备要"先建被依赖的"

★★ 复盘的大忌 ✗✗:

· "学到了很多" ✗(空话) · "下次注意" ✗(没有具体动作)

每一条都要有:现象 → 根因 → 具体改变 ✓✓

★ 一条来自本项目的完整复盘示例 ✓✓✓:

text
坑:并发测试数据被自己的准备工作清掉
现象:10 个线程全部失败,成功数 0 —— 但测试不报错,只是结果不对 ⚠️
根因:prepareBook() 内部调用了 cleanup(),把刚插好的读者删了
改变:① 准备顺序固定为"先建被依赖的"(先书后读者);
     ② 并发测试的断言写"恰好 N 个成功",让数据异常必然暴露 ✓✓

5.2 技术亮点:本项目最值钱的三条 ★★★ ​

★ 按说服力排序 ✓:

#亮点一句话证据
①并发安全用测试证明 ★★★「把 check-then-act 换成条件 UPDATE 原子扣减,并发测试 10 抢 1 恰好 1 个成功」BorrowConcurrencyTest ✓
②幂等设计 ★★★「借还操作全部用条件更新实现幂等,重复归还库存只加一次(并发测试验证)」同上 ✓
③规则配置化 ★★「业务规则全部来自配置,改上限/借期不用发版」LibraryRules + application.yml ✓

★★ 为什么这三条最值钱 ✓✓:

  1. 它们都是"踩过坑才会想到"的设计 —— 证明你理解问题而不只是套模板 ✓
  2. 它们都有可验证的证据 —— 不是"我做了",而是"测试证明" ✓✓
  3. 它们能自然引出追问 —— 你可以引导面试官问到你最熟的地方 ✓✓✓

★ 反例(不要这样提炼) ✗✗:

· "使用了 Spring Boot 框架" ✗(这是技术栈,不是亮点) · "实现了 CRUD 功能" ✗(这是任务清单) · "代码结构清晰" ✗(这是自我评价,没有证据)

5.3 STAR 法则写项目描述 ★★★ ​

★ STAR = Situation(背景)Task(任务)Action(行动)Result(结果) ✓

★ 本项目的完整 STAR ✓✓:

要素内容
S 背景图书借阅场景,核心风险是并发超借(多读者同时抢最后一本)
T 任务设计并实现借阅系统,要求:不超借、不重复归还、规则可调整
A 行动① 库存冗余字段 + 条件 UPDATE 原子扣减;② 归还用条件更新实现幂等;③ 业务规则抽成配置;④ 数据库 CHECK 约束兜底
R 结果30 个测试全绿;并发测试证明 10 抢 1 恰好 1 个成功;重复归还库存只加一次

★★ 写简历的模板 ✓✓:

text
【项目】图书借阅管理系统(个人项目,Spring Boot 3.4 / JdbcTemplate / H2+PostgreSQL)

· 针对并发超借风险,将「查询-判断-更新」改为带条件的原子 UPDATE
  (WHERE available_copies > 0),并以并发测试验证:10 线程抢 1 本,
  恰好 1 个成功、库存恰好归零;
· 借还操作全部按「条件更新」实现幂等,重复归还库存只增加一次;
· 业务规则(借阅上限/借期/续借次数)全部配置化,调整无需发版;
· 以数据库 CHECK 约束兜底库存合法性,应用层校验失败也不会产生脏数据;
  30 个分层测试(领域/接口/集成/并发)全部通过。

★★★ 一条黄金法则 ✓✓✓:

每一条都要能回答"你怎么证明?" ✓

写了"并发安全"→ 追问一定是"怎么验证的?" → 你有并发测试 ✓

写了"幂等"→ 追问"重复请求会怎样?" → 你有条件更新 + 测试 ✓

5.4 预判追问:面试官会问什么 ★★★ ​

★ 本项目的五个高频追问与答法 ✓✓:

追问答法要点
"为什么不用乐观锁/分布式锁?"单库单行场景,条件 UPDATE 就是最简单的原子方案;行锁由数据库保证;引入锁反而增加复杂度 ✓
"库存和借阅记录怎么保证一致?"同一事务;且顺序讲究:借书先原子扣库存(失败直接拒)、还书先标记记录(成功才加库存)✓
"为什么不用 MyBatis/JPA?"本项目要把 SQL 看清;JdbcTemplate 让每条 SQL 可见、可测;选型看目标,不看流行度 ✓
"测试怎么分层的?"四层:领域(毫秒级)/ 接口(协议)/ 集成(真实库)/ 并发;并发的坑只有并发测试能抓 ✓
"如果并发量再大十倍怎么办?"单库行锁会成瓶颈 → 预扣库存到 Redis(《缓存穿透、击穿与雪崩》)或分段库存;但要先说清当前方案在什么量级内是够的 ✓✓

★★ 最后一问的答法结构 ✓✓✓:

先说清"当前方案够用到什么量级",再说"量级大了怎么演进" ✓

而不是一上来就"上 Redis、上分库分表" ✗(那是过度设计的信号)✓

5.5 复盘产出物:一份 ADR 式的记录 ★★ ​

★ 把复盘写成文件,而不是聊完就散 ✓:

text
docs/05-projects/project-a/RETROSPECTIVE.md
  · 做得好的 3 条(各带证据)
  · 踩坑的 5 条(各带 现象/根因/改变)
  · 下次的 3 个具体动作
  · 简历版描述(STAR)
  · 追问清单与答法

★ 本项目的坑已经全部沉淀在两处 ✓:

沉淀处内容
.codebuddy/rules/build-and-verify.md工程类(引号、构建、脚本编码、死链)✓
本节 + 各讲义的"踩坑"段落项目类(Mockito/JDK25、KeyHolder、测试数据顺序)✓

★★ 一句话 ✓✓:坑要么变成约束(下次不再犯),要么变成讲义(变成可教的知识) ✓✓ 否则同一个坑一定会再踩 ✗✗

6. 递进示例 ​

6.1 最小示例:一条带证据的简历条目 ​

text
· 以条件 UPDATE(WHERE available_copies > 0)实现库存原子扣减,
  并发测试证明 10 线程抢 1 本恰好 1 个成功

6.2 业务示例:把踩坑变成面试故事 ​

text
面试官:你在这个项目里遇到最难的问题是什么?

答:测试环境问题。我按文档用 Mockito 写 Service 单测,结果在 JDK 25 上
   全部报"cannot mock this class"。排查后发现是 Mockito 版本对 JDK 25
   的支持问题。我改用真实数据库 + @Transactional 测业务规则 ——
   代价是测试慢一点,但换来的是断言真实数据、不会假通过。
   这让我意识到:测试方案要先验证"在当前环境能不能跑",再谈分层。

6.3 反例:把踩坑说成"环境问题"就完了 ​

text
✗ "当时环境有问题,后来就好了。"
→ 面试官听到的是:你没搞清楚根因 ✗✗

7. 完整可运行代码与实测 ​

复盘依据(全部真实、可复现) ✓:

材料位置
30 个测试labs/project-a/src/test/
并发验证输出BorrowConcurrencyTest 的 System.out
踩坑记录各讲义"踩坑与修复"段 + 约束文件
部署脚本scripts/run-project-a.ps1

★ 自检清单 ✓:

  • [ ] 能在 30 秒内讲清"这个项目是什么、难点在哪" ✓
  • [ ] 三条技术亮点各有测试证据 ✓✓
  • [ ] 五个高频追问都有准备 ✓✓
  • [ ] 踩坑都有"根因 + 改变" ✓✓

8. Debug 观察点 ​

  1. 把你的简历描述给一个没看过项目的人:看他能不能复述出难点 ✓✓;
  2. 对着 STAR 逐条自问"怎么证明":答不上来的删掉或补证据 ★★★;
  3. 模拟一次追问链(亮点 → 细节 → 边界 → 演进):走到哪一步卡住就补哪里 ✓✓;
  4. 检查复盘里有没有"感觉""应该""大概":有就改成具体事实 ✓。

9. 常见错误 ​

#错误说明
1简历写技术栈清单当亮点 ✗✗那是"用过",不是"做过" ✓✓
2亮点没有证据 ✗✗一追问就露馅 ✓✓
3复盘只有感想没有动作 ✗✗下次还会踩同一个坑 ✓✓
4夸大("支撑百万并发")✗✗一追问细节就穿帮 ✓✓
5把坑归结为"环境问题" ✗✗说明没找到根因 ✓
6一上来就讲"我用了多少中间件" ✗先讲问题,再讲方案 ✓✓

10. 动手实验 ​

目标:产出一份能直接用的复盘 + 简历描述。

步骤:

  1. 按 5.1 的框架写出本项目复盘(对照本节答案)✓;
  2. 提炼你自己的 3 条亮点,每条配一个证据 ★★★;
  3. 写 STAR 版项目描述(150 字以内)★★★;
  4. 列出 5 个追问并写答法 ★★★;
  5. 找人模拟 10 分钟面试,记录卡住的地方 ★★★。

验收方式:30 秒内讲清项目 + 每个亮点都有证据。

扩展任务:给项目 B(高并发)提前做设计预演:

  • 列出"并发量 ×10"后这个系统会先在哪里出问题(行锁?连接池?数据库?)✓✓;
  • 写出演进路线(先做什么、后做什么、为什么是这个顺序)★★★;
  • 说明:为什么"演进路线"比"终极架构"更能体现水平(提示:每一步都有依据)★★

11. 练习题 ​

  1. 判断:简历里的项目描述应该尽量多列技术栈。(对 / 错)
  2. 填空:STAR 的四个字母分别代表 、、、。
  3. 简答:为什么"并发不超借"这个亮点比"用了 Spring Boot"值钱?
  4. (改写)把这条弱描述改强:
text
"实现了消息队列功能,解决了系统耦合问题。"
  1. (设计)面试官问"这个项目如果重来一次,你会改什么?",你怎么答?
参考答案(要点)
  1. 错。技术栈是入场券,亮点才是区分度 ✗ —— 每个亮点要有"问题 + 方案 + 证据" ✓✓★
  2. Situation 背景;Task 任务;Action 行动;Result 结果 ✓
  3. 因为它包含"问题认知(并发超借)+ 方案(原子扣减)+ 证据(并发测试)" ✓✓ —— "用了 Spring Boot"任何简历都能写 ✗★
  4. 示例:"下单后的积分/通知链路原本同步执行,改用本地消息表 + MQ 异步化: 本地事务保证「扣款 + 写消息」原子,消息表支持重试与对账; 消费端以唯一索引实现幂等,重复投递不会重复加积分。" ✓✓★
  5. 要点结构:① 先肯定当前选择在当时的合理性(需求、约束)✓; ② 说 1~2 个具体的、有依据的改动(如"Repository 抽成接口,方便 Mock 单测")✓✓; ③ 说明权衡(改动的代价是什么)✓; 切忌:全盘否定当时的设计(说明你没有判断力)或"不改"(说明没有反思)✗✗

12. 掌握标准 ​

  • [ ] 能用三问框架做复盘,且每条有证据 ★★★;
  • [ ] 能提炼 3 条技术亮点(问题 + 方案 + 证据) ★★★;
  • [ ] 能用 STAR 写出 150 字项目描述 ★★★;
  • [ ] 能预判 5 个高频追问并备好答法 ★★★;
  • [ ] 知道坑要么变成约束、要么变成讲义 ★★★。

13. 扩展资源 ​

14. 下一步学习 ​

项目 A 至此完成(7/7)✓。接下来是 《项目 B:高并发场景与架构设计》: 把同样的方法用在一个"并发是主要矛盾"的场景上。

进度只保存在本机浏览器,可随时清除

个人学习项目:AI 生成内容均经人工复核,第三方资源仅作索引