主题
界面
主题
《Spring 源码阅读方法》 练了「容器型」框架(Spring);本节换两种内核风格: MyBatis 是**「代理 + 映射」,Netty 是「事件驱动 + 责任链」**。
★ 两个最小内核全部实测(各 30-50 行) ✓✓:
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 ✓
★ 面试高频追问都指向源码 ✗✓:
| 问题 | 只背用法答不上来的部分 |
|---|---|
| 「MyBatis 的 Mapper 为什么不用实现类?」 | 代理链 + MapperMethod.execute 的分发 ✓✓ |
| 「MyBatis 插件是怎么生效的?」 | 四大对象的代理包装(责任链)✓ |
| 「Netty 的 handler 顺序写反了会怎样?」 | 入站正向 / 出站反向 ✓✓✓ |
| 「为什么 Netty 的性能高?」 | 事件循环 + Pipeline + 零拷贝(组合拳)✓ |
★ 最小内核(实测输出) ✓✓✓:
===== 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 里;追下去就是整条执行链 ✓。
★ 最小内核(实测输出) ✓✓✓:
pipeline = head → [decode, business, encode] → tail
-- 入站事件(channelRead)从 head 往 tail --
[入站] decode(解码)处理:原始字节流
[入站] business(业务处理)处理:原始字节流
-- 出站事件(write)从 tail 往 head --
[出站] encode(编码)处理:响应对象★★ 两个必须记住的结论 ✓✓✓:
★ 与责任链模式的对照 ✓:Netty 的 pipeline 就是责任链 + 方向语义(《设计模式:行为型》 设计模式课的实战版)✓。
| 维度 | MyBatis(代理 + 映射) | Netty(事件驱动 + 责任链) |
|---|---|---|
| 内核机制 | JDK 动态代理 | Pipeline + EventLoop |
| 扩展点 | Interceptor 链(四大对象代理) | Handler(可插拔) |
| 读源码的抓手 | 「方法调用去哪了」 | 「事件往哪个方向走」 |
| 最小复现难度 | 低(30 行代理)✓✓ | 中(要模拟方向)✓ |
| 面试高频点 | 插件原理、一二级缓存 | pipeline 顺序、零拷贝、EventLoop ✓✓ |
① 这个框架要解决的**核心矛盾**是什么?(MyBatis:接口调用 ↔ SQL;Netty:并发 IO ↔ 串行处理)
② 它用什么**机制**解决?(代理 / 事件循环 + 责任链)
③ 我能不能用 50 行复现这个机制?(复现 = 真懂)✓✓✓Proxy.newProxyInstance(loader, new Class<?>[]{UserMapper.class}, (proxy, method, args) -> {
Select select = method.getAnnotation(Select.class);
// 解析 SQL → 绑定参数 → 执行 → 映射结果
});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, "出站"); } // 反向背下「入站正向出站反向」✗(一周就忘,且遇到变体就懵)
正解:写 20 行 pipeline 自己跑一遍,**顺序错一次**亲眼看到问题 ✓✓✓代码:labs/java-deep/src/main/java/cn/kb/learn/deep/reading/MiniFrameworkLesson.java
运行:
cd labs
java -cp java-deep/target/classes cn.kb.learn.deep.reading.MiniFrameworkLesson实测输出(完整):
===== 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(编码)处理:响应对象[encode, business, decode]:入站直接跳过所有 handler ✓✓✓;insert):观察 MapperMethod 的分发逻辑(类型不同,链路不同)✓✓;Executor 包一层代理(插件就是套代理)✓✓✓;| # | 错误 | 说明 |
|---|---|---|
| 1 | 以为 Mapper 接口有隐藏实现类 ✗✗ | 是动态代理,不是生成实现类 ✓✓ |
| 2 | 把 Netty 的 handler 当「顺序函数」 ✗✗ | 入站/出站方向不同 ✓✓ |
| 3 | 插件原理背成「拦截器」三个字 ✗ | 说不清「拦截的是四大对象的哪个」✓✓ |
| 4 | 只读不画图 ✗ | 事件传播不看图记不住 ✓ |
| 5 | 忽略版本差异 ✗ | Netty 4/5、MyBatis 3.4/3.5 细节有别 ✓ |
MiniFrameworkLesson 两个 Part ✓✓✓;MapperProxy.invoke(第一次"见到活的")✓✓✓;验收方式:能画出「Mapper 调用 → 代理 → Executor → StatementHandler → 结果映射」与「Netty 入站/出站传播方向」两张图。
扩展任务:给 MiniFrameworkLesson 加第三个 Part:迷你 EventLoop(单线程轮询 + 任务队列),体会 Netty 的线程模型 ✓✓
《开源项目阅读方法》 开源项目阅读方法 —— 从「单个机制」升级到「整个仓库」。
暂无