Skip to content

MyBatis 与 Netty 源码阅读 ​

S8-002选修高级约 90 分钟JDK 21持续学习 · 源码与开源

1. 本节定位 ​

《Spring 源码阅读方法》 练了「容器型」框架(Spring);本节换两种内核风格: MyBatis 是**「代理 + 映射」,Netty 是「事件驱动 + 责任链」**。

★ 两个最小内核全部实测(各 30-50 行) ✓✓:

text
Part 1 MapperProxy:动态代理把接口方法翻译成 SQL
  invoke:UserMapper.findById(42) → 解析 @Select → 参数绑定 #1 = 42 → 映射结果 ✓✓
Part 2 ChannelPipeline:入站正向、出站反向
  [入站] decode → business(head 往 tail);[出站] encode(tail 往 head)✓✓

★ 代码:labs/java-deep/src/main/java/cn/kb/learn/deep/reading/MiniFrameworkLesson.java ✓

2. 学习目标 ​

  • 能说清 Mapper 接口「没有实现类」却能用 —— 代理链是怎么串的 ★★★;
  • 理解 MyBatis 的四大对象与职责边界(Executor / StatementHandler / ParameterHandler / ResultSetHandler)★★;
  • 能说清 Netty Pipeline 的入站/出站传播方向与顺序依赖 ★★★;
  • 会用「最小内核」方法读不同风格的框架源码 ★★★。

3. 前置知识检查 ​

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

★ 面试高频追问都指向源码 ✗✓:

问题只背用法答不上来的部分
「MyBatis 的 Mapper 为什么不用实现类?」代理链 + MapperMethod.execute 的分发 ✓✓
「MyBatis 插件是怎么生效的?」四大对象的代理包装(责任链)✓
「Netty 的 handler 顺序写反了会怎样?」入站正向 / 出站反向 ✓✓✓
「为什么 Netty 的性能高?」事件循环 + Pipeline + 零拷贝(组合拳)✓

5. 核心概念 ​

5.1 实测 Part 1:MapperProxy(代理 + 映射)★★★ ​

★ 最小内核(实测输出) ✓✓✓:

text
===== Part 1:MyBatis 的 MapperProxy(动态代理把接口翻译成 SQL)=====
    invoke:UserMapper.findById(42)
    解析 @Select:SELECT id, name FROM users WHERE id = ?
    参数绑定: #1 = 42
    执行(模拟 JDBC)→ 映射结果
    最终结果:User[id=42, name=用户42] ✓✓

★ 对应真实源码的链路 ✓✓:MapperProxyFactory.newInstance → MapperProxy.invoke → MapperMethod.execute(根据方法类型分发 SELECT/INSERT…)→ DefaultSqlSession → Executor → StatementHandler(拼 SQL)→ ParameterHandler(绑参)→ ResultSetHandler(映射结果)✓。

★★ 读源码的切入点 ✓✓✓:先问「接口没有实现类,那方法调用去哪了?」 —— 答案在 invoke 里;追下去就是整条执行链 ✓。

5.2 实测 Part 2:ChannelPipeline(事件驱动 + 责任链)★★★ ​

★ 最小内核(实测输出) ✓✓✓:

text
    pipeline = head → [decode, business, encode] → tail
    -- 入站事件(channelRead)从 head 往 tail --
      [入站] decode(解码)处理:原始字节流
      [入站] business(业务处理)处理:原始字节流
    -- 出站事件(write)从 tail 往 head --
      [出站] encode(编码)处理:响应对象

★★ 两个必须记住的结论 ✓✓✓:

  1. 入站从 head 往 tail(正向),出站从 tail 往 head(反向) —— 所以「decode 在前、encode 在后」的顺序是刻意的 ✓✓;
  2. handler 顺序写反,出站事件会「找不到 handler」 —— 这类线上问题只能靠理解传播方向解释 ✓✓✓。

★ 与责任链模式的对照 ✓:Netty 的 pipeline 就是责任链 + 方向语义(《设计模式:行为型》 设计模式课的实战版)✓。

5.3 两种框架风格对照 ★★ ​

维度MyBatis(代理 + 映射)Netty(事件驱动 + 责任链)
内核机制JDK 动态代理Pipeline + EventLoop
扩展点Interceptor 链(四大对象代理)Handler(可插拔)
读源码的抓手「方法调用去哪了」「事件往哪个方向走」
最小复现难度低(30 行代理)✓✓中(要模拟方向)✓
面试高频点插件原理、一二级缓存pipeline 顺序、零拷贝、EventLoop ✓✓

5.4 通用方法:最小内核三问 ★★★ ​

text
① 这个框架要解决的**核心矛盾**是什么?(MyBatis:接口调用 ↔ SQL;Netty:并发 IO ↔ 串行处理)
② 它用什么**机制**解决?(代理 / 事件循环 + 责任链)
③ 我能不能用 50 行复现这个机制?(复现 = 真懂)✓✓✓

6. 递进示例 ​

6.1 最小代理(去掉框架也能写) ​

java
Proxy.newProxyInstance(loader, new Class<?>[]{UserMapper.class}, (proxy, method, args) -> {
    Select select = method.getAnnotation(Select.class);
    // 解析 SQL → 绑定参数 → 执行 → 映射结果
});

6.2 最小 Pipeline ​

java
void fireChannelRead(Object message) { for (Handler h : handlers) h.handle(message, "入站"); }   // 正向
void write(Object message) { for (int i = size-1; i >= 0; i--) handlers.get(i).handle(message, "出站"); }  // 反向

6.3 反例:只看图不写复现 ​

text
背下「入站正向出站反向」✗(一周就忘,且遇到变体就懵)
正解:写 20 行 pipeline 自己跑一遍,**顺序错一次**亲眼看到问题 ✓✓✓

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

代码:labs/java-deep/src/main/java/cn/kb/learn/deep/reading/MiniFrameworkLesson.java

运行:

powershell
cd labs
java -cp java-deep/target/classes cn.kb.learn.deep.reading.MiniFrameworkLesson

实测输出(完整):

text
===== Part 1:MyBatis 的 MapperProxy(动态代理把接口翻译成 SQL)=====
    invoke:UserMapper.findById(42)
    解析 @Select:SELECT id, name FROM users WHERE id = ?
    参数绑定: #1 = 42
    执行(模拟 JDBC)→ 映射结果
    最终结果:User[id=42, name=用户42] ✓✓

===== Part 2:Netty 的 ChannelPipeline(入站正向、出站反向)=====
    pipeline = head → [decode, business, encode] → tail
    -- 入站事件(channelRead)从 head 往 tail --
      [入站] decode(解码)处理:原始字节流
      [入站] business(业务处理)处理:原始字节流
    -- 出站事件(write)从 tail 往 head --
      [出站] encode(编码)处理:响应对象

8. Debug 观察点 ​

  1. 把 pipeline 的 handler 顺序改成 [encode, business, decode]:入站直接跳过所有 handler ✓✓✓;
  2. 给 Mapper 代理加第二个方法(insert):观察 MapperMethod 的分发逻辑(类型不同,链路不同)✓✓;
  3. 复现 MyBatis 插件:给 Executor 包一层代理(插件就是套代理)✓✓✓;
  4. 用 《MyBatis 数据持久化》 的真实 MyBatis 实验,对照打印 SQL 日志 ✓。

9. 常见错误 ​

#错误说明
1以为 Mapper 接口有隐藏实现类 ✗✗是动态代理,不是生成实现类 ✓✓
2把 Netty 的 handler 当「顺序函数」 ✗✗入站/出站方向不同 ✓✓
3插件原理背成「拦截器」三个字 ✗说不清「拦截的是四大对象的哪个」✓✓
4只读不画图 ✗事件传播不看图记不住 ✓
5忽略版本差异 ✗Netty 4/5、MyBatis 3.4/3.5 细节有别 ✓

10. 动手实验 ​

  1. 跑 MiniFrameworkLesson 两个 Part ✓✓✓;
  2. 把管道顺序改错,观察事件丢失(记录现象)✗✗✓;
  3. 用真实 MyBatis 打个断点,走到 MapperProxy.invoke(第一次"见到活的")✓✓✓;
  4. 写一个最小 Interceptor(打印执行耗时),体验插件链 ✓✓。

验收方式:能画出「Mapper 调用 → 代理 → Executor → StatementHandler → 结果映射」与「Netty 入站/出站传播方向」两张图。

扩展任务:给 MiniFrameworkLesson 加第三个 Part:迷你 EventLoop(单线程轮询 + 任务队列),体会 Netty 的线程模型 ✓✓

11. 练习题 ​

  1. Mapper 接口没有实现类,方法调用去哪了?
  2. MyBatis 插件的本质是什么?(拦截哪些对象)
  3. Netty 的入站与出站传播方向?顺序写反会怎样?
  4. 两种框架的核心矛盾与机制分别是什么?
  5. 「最小内核三问」是什么?
参考答案(要点)
  1. JDK 动态代理 → MapperProxy.invoke → MapperMethod.execute ✓✓★
  2. 对四大对象(Executor/StatementHandler/ParameterHandler/ResultSetHandler)做代理包装 ✓✓★
  3. 入站 head→tail、出站 tail→head;顺序错 = 事件丢失 ✓✓★
  4. MyBatis:接口调用 ↔ SQL,用代理 + 映射解决;Netty:并发 IO ↔ 串行处理,用 EventLoop + Pipeline ✓✓★
  5. 核心矛盾 / 机制 / 能否 50 行复现 ✓✓★

12. 掌握标准 ​

  • [ ] 能画出 Mapper 调用链路 ★★★;
  • [ ] 能画出 Pipeline 传播方向 ★★★;
  • [ ] 会写最小代理与最小责任链 ★★★;
  • [ ] 知道插件的代理包装原理 ★★;
  • [ ] 掌握「最小内核三问」★★★。

13. 扩展资源 ​

14. 下一步学习 ​

《开源项目阅读方法》 开源项目阅读方法 —— 从「单个机制」升级到「整个仓库」。

后续知识

暂无

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

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