ChannelInitializer 工作原理详解
关键理解:两个阶段
阶段一:添加 ChannelInitializer 到 pipeline
// 在 ServerBootstrap 中
b.childHandler(new MyChannelInitializer())
此时发生了什么:
Pipeline 状态:
head -> MyChannelInitializer -> tail
注意:
- LineBasedFrameDecoder 还没添加
- StringDecoder 还没添加
- StringEncoder 还没添加
- MyServerHandler 还没添加
只有 MyChannelInitializer 这一个 handler 在 pipeline 中!
阶段二:register 时触发 ChannelInitializer.handlerAdded()
当 invokeHandlerAddedIfNeeded() 被调用时:
// 1. 触发 MyChannelInitializer 的 handlerAdded() 方法
// (这个方法在父类 ChannelInitializer 中)
MyChannelInitializer.handlerAdded(ctx) // 继承自父类
// 2. handlerAdded() 内部会调用你重写的 initChannel() 方法
protected void initChannel(SocketChannel channel) {
// 现在才真正添加这些 handlers
channel.pipeline().addLast(new LineBasedFrameDecoder(1024));
channel.pipeline().addLast(new StringDecoder(...));
channel.pipeline().addLast(new StringEncoder(...));
channel.pipeline().addLast(new MyServerHandler());
}
// 3. initChannel() 执行完后,ChannelInitializer 把自己从 pipeline 中移除
最终 Pipeline 状态:
head -> LineBasedFrameDecoder -> StringDecoder -> StringEncoder -> MyServerHandler -> tail
注意:MyChannelInitializer 已经不在 pipeline 中了!
详细时序图
时间线:从启动到 pipeline 构建完成
1. ServerBootstrap 启动
↓
b.childHandler(new MyChannelInitializer())
↓
【此时】Pipeline: head -> MyChannelInitializer -> tail
↓
2. 客户端连接,创建 SocketChannel
↓
init(channel) - 将 MyChannelInitializer 添加到新 channel 的 pipeline
↓
【此时】新 channel 的 Pipeline: head -> MyChannelInitializer -> tail
↓
3. register 操作
↓
doRegister() - 注册到 Selector
↓
invokeHandlerAddedIfNeeded() ⭐ 关键点
↓
4. 执行 MyChannelInitializer.handlerAdded()
(这个方法在父类 ChannelInitializer 中实现)
↓
handlerAdded() 内部调用 initChannel(channel)
↓
5. 执行你重写的 initChannel() 方法
↓
pipeline.addLast(new LineBasedFrameDecoder(1024))
pipeline.addLast(new StringDecoder(...))
pipeline.addLast(new StringEncoder(...))
pipeline.addLast(new MyServerHandler())
↓
6. ChannelInitializer 移除自己
↓
【最终】Pipeline: head -> LineBasedFrameDecoder -> StringDecoder
-> StringEncoder -> MyServerHandler -> tail
源码验证
1. ChannelInitializer 的 handlerAdded() 方法
// ChannelInitializer 类(你的 MyChannelInitializer 的父类)
public abstract class ChannelInitializer<C extends Channel> extends ChannelInboundHandlerAdapter {
// 这就是被 invokeHandlerAddedIfNeeded() 触发的方法!
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
if (ctx.channel().isRegistered()) {
// 这里会调用你重写的 initChannel 方法
if (initChannel(ctx)) {
// 初始化完成后,移除自己
removeState(ctx);
}
}
}
private boolean initChannel(ChannelHandlerContext ctx) throws Exception {
if (initMap.add(ctx)) {
try {
// ⭐ 调用你重写的 initChannel 方法
initChannel((C) ctx.channel());
} catch (Throwable cause) {
exceptionCaught(ctx, cause);
} finally {
// ⭐ 从 pipeline 中移除 ChannelInitializer
ChannelPipeline pipeline = ctx.pipeline();
if (pipeline.context(this) != null) {
pipeline.remove(this);
}
}
return true;
}
return false;
}
// 这是你需要重写的抽象方法
protected abstract void initChannel(C ch) throws Exception;
}
2. 你的 MyChannelInitializer
public class MyChannelInitializer extends ChannelInitializer<SocketChannel> {
// 你只需要重写这个方法
@Override
protected void initChannel(SocketChannel channel) {
// 这个方法会在父类的 handlerAdded() 中被调用
channel.pipeline().addLast(new LineBasedFrameDecoder(1024));
channel.pipeline().addLast(new StringDecoder(Charset.forName("GBK")));
channel.pipeline().addLast(new StringEncoder(Charset.forName("GBK")));
channel.pipeline().addLast(new MyServerHandler());
}
// 注意:你不需要重写 handlerAdded(),因为父类已经实现了
}
时序:
// 1. invokeHandlerAddedIfNeeded() 触发
MyChannelInitializer.handlerAdded() // 父类的方法
// 2. 在 handlerAdded() 内部调用
initChannel(channel) // 你重写的方法
// 3. 在 initChannel() 中
pipeline.addLast(new LineBasedFrameDecoder(1024))
↓
// 这时会立即触发 LineBasedFrameDecoder.handlerAdded()
pipeline.addLast(new StringDecoder(...))
↓
// 这时会立即触发 StringDecoder.handlerAdded()
// 以此类推...
触发了两层 handlerAdded():
第一层(直接触发):
MyChannelInitializer.handlerAdded() // 继承自父类 ChannelInitializer
第二层(间接触发):
// 在 MyChannelInitializer.handlerAdded() 内部
// 调用 initChannel() 时,每添加一个 handler 都会触发它的 handlerAdded()
LineBasedFrameDecoder.handlerAdded()
StringDecoder.handlerAdded()
StringEncoder.handlerAdded()
MyServerHandler.handlerAdded()
完整的调用链
register0()
↓
invokeHandlerAddedIfNeeded()
↓
callHandlerAddedForAllHandlers()
↓
PendingHandlerAddedTask.execute()
↓
callHandlerAdded0(MyChannelInitializer的context)
↓
MyChannelInitializer.handlerAdded() // 父类 ChannelInitializer 的方法
↓
initChannel(channel) // 调用你重写的方法
↓
pipeline.addLast(new LineBasedFrameDecoder(1024))
↓
callHandlerAdded0(LineBasedFrameDecoder的context)
↓
LineBasedFrameDecoder.handlerAdded()
pipeline.addLast(new StringDecoder(...))
↓
callHandlerAdded0(StringDecoder的context)
↓
StringDecoder.handlerAdded()
// ... 以此类推
最后:
pipeline.remove(MyChannelInitializer) // 移除自己
为什么要这样设计?
1. 延迟初始化
// 启动时只添加 ChannelInitializer
b.childHandler(new MyChannelInitializer())
// 等到 channel 真正 register 后,才添加真正的 handlers
// 这样可以确保在正确的时机、正确的线程中初始化
2. 代码复用
// 你只需要关心添加哪些 handlers
protected void initChannel(SocketChannel channel) {
channel.pipeline().addLast(...);
}
// 不需要关心何时调用、如何移除等细节
// 这些都由父类 ChannelInitializer 处理
3. 自动清理
// ChannelInitializer 完成任务后自动移除自己
// 不会占用 pipeline 的空间
// 最终 pipeline 中只有真正的业务 handlers
实际运行示例
假设你启动服务器,有客户端连接:
【步骤1】服务器启动
Pipeline: head -> MyChannelInitializer -> tail
【步骤2】客户端连接,触发 register
invokeHandlerAddedIfNeeded() 被调用
【步骤3】执行 MyChannelInitializer.handlerAdded()
进入你的 initChannel() 方法
【步骤4】添加第一个 handler
pipeline.addLast(new LineBasedFrameDecoder(1024))
Pipeline: head -> MyChannelInitializer -> LineBasedFrameDecoder -> tail
触发 LineBasedFrameDecoder.handlerAdded()
【步骤5】添加第二个 handler
pipeline.addLast(new StringDecoder(...))
Pipeline: head -> MyChannelInitializer -> LineBasedFrameDecoder -> StringDecoder -> tail
触发 StringDecoder.handlerAdded()
【步骤6】添加第三个 handler
pipeline.addLast(new StringEncoder(...))
Pipeline: head -> MyChannelInitializer -> LineBasedFrameDecoder
-> StringDecoder -> StringEncoder -> tail
触发 StringEncoder.handlerAdded()
【步骤7】添加第四个 handler
pipeline.addLast(new MyServerHandler())
Pipeline: head -> MyChannelInitializer -> LineBasedFrameDecoder
-> StringDecoder -> StringEncoder -> MyServerHandler -> tail
触发 MyServerHandler.handlerAdded()
【步骤8】移除 ChannelInitializer
pipeline.remove(MyChannelInitializer)
最终 Pipeline: head -> LineBasedFrameDecoder -> StringDecoder
-> StringEncoder -> MyServerHandler -> tail
总结
核心理解:
-
ChannelInitializer 是一个特殊的 handler
- 它的作用是添加其他 handlers
- 完成任务后会自动移除自己
-
invokeHandlerAddedIfNeeded() 直接触发的是 ChannelInitializer.handlerAdded()
- 这个方法在父类中实现
- 你不需要重写它
-
你重写的 initChannel() 会被 handlerAdded() 调用
- 在 initChannel() 中添加的每个 handler
- 都会立即触发它自己的 handlerAdded()
-
最终结果
- ChannelInitializer 消失
- 你添加的 handlers 都在 pipeline 中
- 每个 handler 的 handlerAdded() 都被调用过了
简单比喻
ChannelInitializer 就像一个"装修工人":
-
雇佣阶段(添加到 pipeline)
b.childHandler(new MyChannelInitializer()) // 此时:只有装修工人进场了 // Pipeline: head -> 装修工人 -> tail -
开工信号(invokeHandlerAddedIfNeeded)
// register 完成后,告诉装修工人:"可以开始干活了!" 装修工人.handlerAdded() // 父类的方法 -
装修工人开始干活(调用你的 initChannel)
protected void initChannel(SocketChannel channel) { // 装修工人开始搬运真正的设备进场 channel.pipeline().addLast(new LineBasedFrameDecoder(1024)); // 搬进设备1 channel.pipeline().addLast(new StringDecoder(...)); // 搬进设备2 channel.pipeline().addLast(new StringEncoder(...)); // 搬进设备3 channel.pipeline().addLast(new MyServerHandler()); // 搬进设备4 } -
装修工人离场
// 设备都搬进来了,装修工人的任务完成,自己离开 // 最终 Pipeline: head -> 设备1 -> 设备2 -> 设备3 -> 设备4 -> tail
关键点:
invokeHandlerAddedIfNeeded()触发的是"装修工人"(ChannelInitializer)的handlerAdded()- "装修工人"的
handlerAdded()在父类中已经实现好了,你不需要写 - 你只需要告诉"装修工人"要搬哪些"设备"进来(重写
initChannel()) - 每个"设备"搬进来时,也会触发它自己的
handlerAdded()
4887



被折叠的 条评论
为什么被折叠?



