initChannel 方法调用流程详解
前言
当 handler().handlerAdded(this) 被调用时,调用的是哪个 ChannelInitializer 实例的方法?
本文将深入剖析这个问题,带你理解 Netty 中两个 ChannelInitializer 实例的调用关系。
让我们先回顾一下关键代码:
final void callHandlerAdded() throws Exception {
if (setAddComplete()) {
// 这里调用的是哪个 Handler 的 handlerAdded 方法?
handler().handlerAdded(this);
}
}
这个问题的答案是:调用的是 Bootstrap.init() 方法中创建的匿名 ChannelInitializer 实例的 handlerAdded() 方法。
为什么呢?让我们一步步分析。
理解关键:Context 持有的是哪个 Handler?
回顾 Pipeline 初始化的准备阶段
在 Bootstrap.init() 方法中,Netty 创建了一个匿名 ChannelInitializer:
@Override
void init(Channel channel) throws Exception {
ChannelPipeline p = channel.pipeline();
// 创建匿名 ChannelInitializer(我们称它为 Initializer-A)
p.addLast(new ChannelInitializer<Channel>() {
@Override
public void initChannel(final Channel ch) throws Exception {
final ChannelPipeline pipeline = ch.pipeline();
// 获取用户设置的 handler(MyChannelInitializer)
ChannelHandler handler = config.handler();
if (handler != null) {
// 将 MyChannelInitializer 添加到 Pipeline
pipeline.addLast(handler);
}
}
});
}
关键点:addLast() 做了什么?
当调用 p.addLast(new ChannelInitializer<Channel>() {...}) 时:
- 创建了一个新的 Context(我们称它为 ctx-A)
- ctx-A 持有的 handler 引用指向这个匿名 ChannelInitializer(Initializer-A)
- ctx-A 被添加到 Pipeline 链表中
- 创建了一个 PendingHandlerAddedTask,持有 ctx-A 的引用
此时的状态:
Pipeline: head <-> ctx-A(持有 Initializer-A) <-> tail
待处理任务: PendingHandlerAddedTask(ctx-A)
执行阶段:调用 handlerAdded
当 Channel 注册完成后,会执行待处理任务:
// PendingHandlerAddedTask.execute()
void execute() {
callHandlerAdded0(ctx); // 这里的 ctx 就是 ctx-A
}
// callHandlerAdded0
private void callHandlerAdded0(final AbstractChannelHandlerContext ctx) {
ctx.callHandlerAdded(); // 调用 ctx-A 的 callHandlerAdded
}
// AbstractChannelHandlerContext.callHandlerAdded
final void callHandlerAdded() throws Exception {
if (setAddComplete()) {
// 这里的 handler() 返回的是 ctx-A 持有的 handler
// 也就是 Initializer-A(匿名 ChannelInitializer)
handler().handlerAdded(this);
}
}
关键理解:
handler()方法返回的是当前 Context 持有的 Handler 引用- ctx-A 持有的是 Initializer-A(匿名 ChannelInitializer)
- 所以
handler().handlerAdded(this)调用的是 Initializer-A 的 handlerAdded() 方法
完整的调用流程
现在让我们完整地追踪整个调用流程,看看 Initializer-A 的 handlerAdded() 方法是如何最终调用到我们自定义的 MyChannelInitializer 的 initChannel() 方法的。
第一步:调用 Initializer-A 的 handlerAdded()
// ChannelInitializer.handlerAdded()
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
// 检查 Channel 是否已经注册
if (ctx.channel().isRegistered()) {
// 调用 initChannel(ctx)
if (initChannel(ctx)) {
// 初始化成功后,移除自己
removeState(ctx);
}
}
}
作用:
这个方法是 ChannelInitializer 的核心方法,它会:
- 检查 Channel 是否已经注册(此时已经注册)
- 调用
initChannel(ctx)方法 - 初始化完成后,将自己从 Pipeline 中移除
第二步:调用 initChannel(ChannelHandlerContext)
// ChannelInitializer.initChannel(ChannelHandlerContext)
private boolean initChannel(ChannelHandlerContext ctx) throws Exception {
// 使用 Set 确保每个 Context 只初始化一次
if (initMap.add(ctx)) {
try {
// 关键:调用抽象方法 initChannel(Channel)
// 由于多态,这里会调用子类(Initializer-A)重写的版本
initChannel((C) ctx.channel());
} catch (Throwable cause) {
// 如果初始化过程中出现异常,触发异常处理
exceptionCaught(ctx, cause);
} finally {
// 无论成功还是失败,都要将 ChannelInitializer 从 Pipeline 中移除
ChannelPipeline pipeline = ctx.pipeline();
if (pipeline.context(this) != null) {
pipeline.remove(this);
}
}
return true;
}
return false;
}
作用:
这个方法做了三件重要的事:
- 使用
initMap确保每个 Context 只初始化一次(防止重复初始化) - 调用抽象方法
initChannel(Channel),由于 Java 的多态机制,会调用 Initializer-A 重写的版本 - 在 finally 块中移除 ChannelInitializer,确保它只执行一次
为什么要移除?
ChannelInitializer 是一个"一次性"的 Handler,它的唯一作用就是在合适的时机初始化 Pipeline。一旦初始化完成,它就没有存在的必要了,所以要从 Pipeline 中移除。
第三步:调用 Initializer-A 重写的 initChannel(Channel)
// 这是在 Bootstrap.init() 中定义的匿名内部类
new ChannelInitializer<Channel>() {
@Override
public void initChannel(final Channel ch) throws Exception {
final ChannelPipeline pipeline = ch.pipeline();
// 获取用户设置的 handler(MyChannelInitializer)
ChannelHandler handler = config.handler();
if (handler != null) {
// 将 MyChannelInitializer 添加到 Pipeline
pipeline.addLast(handler);
}
// 注意:这里可能还有其他逻辑,比如添加 ServerBootstrapAcceptor
// 但对于客户端来说,主要就是添加用户的 handler
}
}
作用:
这是 Initializer-A 重写的 initChannel() 方法,它的作用是:
- 获取用户通过
b.handler()设置的 Handler(也就是 MyChannelInitializer) - 将 MyChannelInitializer 添加到 Pipeline 中
关键理解:
config.handler()返回的是我们在客户端启动代码中设置的new MyChannelInitializer()- 这里调用
pipeline.addLast(handler)会将 MyChannelInitializer 添加到 Pipeline
此时 Pipeline 的状态变化:
之前:head <-> Initializer-A <-> tail
之后:head <-> MyChannelInitializer <-> tail
(Initializer-A 在 finally 块中被移除)
MyChannelInitializer 的初始化流程
现在,MyChannelInitializer 已经被添加到 Pipeline 中了。接下来,它也会经历同样的初始化流程。
第四步:addLast(MyChannelInitializer)
当调用 pipeline.addLast(handler) 时(这里的 handler 是 MyChannelInitializer):
@Override
public final ChannelPipeline addLast(ChannelHandler... handlers) {
return addLast(null, handlers);
}
public final ChannelPipeline addLast(EventExecutorGroup executor, ChannelHandler... handlers) {
if (handlers == null) {
throw new NullPointerException("handlers");
}
for (ChannelHandler h: handlers) {
if (h == null) {
break;
}
// 遍历每个 Handler,逐个添加
addLast(executor, null, h);
}
return this;
}
最终会调用到:
@Override
public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
final AbstractChannelHandlerContext newCtx;
synchronized (this) {
// 检查 Handler 是否可以被多次添加
checkMultiplicity(handler);
// 将 MyChannelInitializer 包装成 Context(我们称它为 ctx-B)
newCtx = newContext(group, filterName(name, handler), handler);
// 将 ctx-B 添加到 Pipeline 链表中
addLast0(newCtx);
// 关键判断:此时 Channel 已经注册(registered = true)
if (!registered) {
// 如果还没注册,创建待处理任务
newCtx.setAddPending();
callHandlerCallbackLater(newCtx, true);
return this;
}
// 因为已经注册,所以直接调用 handlerAdded
EventExecutor executor = newCtx.executor();
if (!executor.inEventLoop()) {
// 如果不在 EventLoop 线程中,提交任务
callHandlerAddedInEventLoop(newCtx, executor);
return this;
}
}
// 在 EventLoop 线程中,直接调用
callHandlerAdded0(newCtx);
return this;
}
关键区别:
与 Initializer-A 不同,此时 Channel 已经注册(registered = true),所以不会创建待处理任务,而是直接调用 callHandlerAdded0(newCtx)。
第五步:调用 callHandlerAdded0(ctx-B)
private void callHandlerAdded0(final AbstractChannelHandlerContext ctx) {
try {
// 调用 ctx-B 的 callHandlerAdded
ctx.callHandlerAdded();
} catch (Throwable t) {
// 异常处理:如果出现异常,移除这个 Handler
boolean removed = false;
try {
remove0(ctx);
ctx.callHandlerRemoved();
removed = true;
} catch (Throwable t2) {
if (logger.isWarnEnabled()) {
logger.warn("Failed to remove a handler: " + ctx.name(), t2);
}
}
// 触发异常事件
if (removed) {
fireExceptionCaught(new ChannelPipelineException(
ctx.handler().getClass().getName() +
".handlerAdded() has thrown an exception; removed.", t));
} else {
fireExceptionCaught(new ChannelPipelineException(
ctx.handler().getClass().getName() +
".handlerAdded() has thrown an exception; also failed to remove.", t));
}
}
}
作用:
调用 ctx-B 的 callHandlerAdded() 方法,并处理可能出现的异常。
第六步:调用 ctx-B.callHandlerAdded()
final void callHandlerAdded() throws Exception {
// 设置状态为 ADD_COMPLETE,确保 handlerAdded 只被调用一次
if (setAddComplete()) {
// 这里的 handler() 返回的是 ctx-B 持有的 handler
// 也就是 MyChannelInitializer
handler().handlerAdded(this);
}
}
关键理解:
- 这里的
handler()返回的是 ctx-B 持有的 Handler - ctx-B 持有的是 MyChannelInitializer
- 所以这里调用的是 MyChannelInitializer 的
handlerAdded()方法
第七步:调用 MyChannelInitializer 的 handlerAdded()
// ChannelInitializer.handlerAdded()
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
if (ctx.channel().isRegistered()) {
// 调用 initChannel(ctx)
if (initChannel(ctx)) {
removeState(ctx);
}
}
}
这个方法和 Initializer-A 的 handlerAdded() 是同一个方法(都是 ChannelInitializer 类的方法)。
第八步:调用 initChannel(ChannelHandlerContext)
private boolean initChannel(ChannelHandlerContext ctx) throws Exception {
if (initMap.add(ctx)) {
try {
// 调用抽象方法 initChannel(Channel)
// 由于多态,这里会调用 MyChannelInitializer 重写的版本
initChannel((C) ctx.channel());
} catch (Throwable cause) {
exceptionCaught(ctx, cause);
} finally {
// 将 MyChannelInitializer 从 Pipeline 中移除
ChannelPipeline pipeline = ctx.pipeline();
if (pipeline.context(this) != null) {
pipeline.remove(this);
}
}
return true;
}
return false;
}
第九步:调用 MyChannelInitializer 重写的 initChannel(Channel)
// 这是我们自定义的 MyChannelInitializer
public class MyChannelInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel channel) throws Exception {
// 这里是我们的业务逻辑
System.out.println("链接报告开始");
System.out.println("链接报告信息:本客户端链接到服务端。channelId:" + channel.id());
System.out.println("链接报告完毕");
// 我们可以在这里添加其他 Handler
// channel.pipeline().addLast(new MyHandler1());
// channel.pipeline().addLast(new MyHandler2());
}
}
作用:
这就是我们自定义的初始化逻辑!在这里,我们可以:
- 打印日志
- 添加编解码器
- 添加业务 Handler
- 进行其他初始化操作
执行完成后,MyChannelInitializer 也会在 finally 块中被移除。
完整流程图
让我们用一张图来总结整个调用流程:
时间线:T1 - 客户端启动
|
v
b.handler(new MyChannelInitializer())
[MyChannelInitializer 实例被保存在 config 中]
-------------------------------------------------------------------
时间线:T2 - 调用 b.connect()
|
v
Bootstrap.init(channel)
|
v
创建 Initializer-A(匿名 ChannelInitializer)
|
v
p.addLast(Initializer-A)
|
v
创建 ctx-A,持有 Initializer-A 的引用
|
v
将 ctx-A 添加到 Pipeline
|
v
创建 PendingHandlerAddedTask(ctx-A)
Pipeline 状态:head <-> ctx-A(Initializer-A) <-> tail
-------------------------------------------------------------------
时间线:T3 - Channel 注册完成
|
v
执行 PendingHandlerAddedTask
|
v
callHandlerAdded0(ctx-A)
|
v
ctx-A.callHandlerAdded()
|
v
ctx-A.handler().handlerAdded(ctx-A)
[这里的 handler() 返回 Initializer-A]
|
v
Initializer-A.handlerAdded(ctx-A)
|
v
Initializer-A.initChannel(ctx-A)
|
v
Initializer-A.initChannel(channel) ← 调用匿名内部类重写的方法
|
v
获取 config.handler() [返回 MyChannelInitializer]
|
v
pipeline.addLast(MyChannelInitializer)
|
v
Initializer-A 被移除
Pipeline 状态:head <-> ctx-B(MyChannelInitializer) <-> tail
-------------------------------------------------------------------
时间线:T4 - MyChannelInitializer 被添加
|
v
创建 ctx-B,持有 MyChannelInitializer 的引用
|
v
因为 registered = true,直接调用 callHandlerAdded0(ctx-B)
|
v
ctx-B.callHandlerAdded()
|
v
ctx-B.handler().handlerAdded(ctx-B)
[这里的 handler() 返回 MyChannelInitializer]
|
v
MyChannelInitializer.handlerAdded(ctx-B)
|
v
MyChannelInitializer.initChannel(ctx-B)
|
v
MyChannelInitializer.initChannel(channel) ← 调用我们重写的方法
|
v
执行我们的业务逻辑:
- 打印日志
- 添加其他 Handler
|
v
MyChannelInitializer 被移除
最终 Pipeline 状态:head <-> [我们添加的 Handler] <-> tail
核心要点总结
1. 两个不同的 ChannelInitializer 实例
-
Initializer-A:Bootstrap.init() 中创建的匿名内部类
- 作用:将用户设置的 Handler(MyChannelInitializer)添加到 Pipeline
- 生命周期:在 register 完成后被调用,执行完立即移除
-
MyChannelInitializer:用户自定义的 ChannelInitializer
- 作用:执行用户的初始化逻辑,添加业务 Handler
- 生命周期:被 Initializer-A 添加到 Pipeline 后立即被调用,执行完立即移除
2. 为什么 callHandlerAdded() 调用的是 Initializer-A?
因为:
- 在准备阶段,
p.addLast(Initializer-A)创建了 ctx-A - ctx-A 持有的 handler 引用指向 Initializer-A
- PendingHandlerAddedTask 持有的是 ctx-A 的引用
- 执行阶段调用
ctx-A.handler().handlerAdded()时,返回的是 Initializer-A
关键理解:Context 持有哪个 Handler 的引用,handler() 方法就返回哪个 Handler。
3. registered 标志的作用
registered 标志决定了 Handler 的初始化时机:
-
registered = false(Channel 还没注册)
- 创建 PendingHandlerAddedTask,延迟到注册完成后执行
- Initializer-A 就是这种情况
-
registered = true(Channel 已经注册)
- 直接调用
callHandlerAdded0(),立即执行初始化 - MyChannelInitializer 就是这种情况
- 直接调用
4. ChannelInitializer 的自动移除机制
ChannelInitializer 在 initChannel(ChannelHandlerContext) 方法的 finally 块中会自动移除自己:
finally {
ChannelPipeline pipeline = ctx.pipeline();
if (pipeline.context(this) != null) {
pipeline.remove(this);
}
}
这确保了 ChannelInitializer 只执行一次,执行完就从 Pipeline 中消失。
5. 多态机制的应用
整个流程中,Java 的多态机制起到了关键作用:
// ChannelInitializer 中的代码
private boolean initChannel(ChannelHandlerContext ctx) throws Exception {
// 调用抽象方法
initChannel((C) ctx.channel());
}
虽然这里调用的是抽象方法 initChannel(Channel),但由于多态:
- 如果当前实例是 Initializer-A,就调用 Initializer-A 重写的版本
- 如果当前实例是 MyChannelInitializer,就调用 MyChannelInitializer 重写的版本
1702



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



