ChannelInitializer是一個PPLine的初始化工具,可以往PPLine裏面設置Handler。
protected abstract void initChannel(C ch) throws Exception;
我們可以重寫此方法來完成初始化動作,往PPLine當中加入Handler。
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
System.out.println("initChannel:" + ch.localAddress());
ch.pipeline().addLast("log", new LoggingHandler(LogLevel.INFO));
ch.pipeline().addLast("DiscardHandler", new DiscardHandler());
ch.pipeline().addLast("hexEncoder2", new HexEncoder2());
ch.pipeline().addLast("hexEncoder1", new HexEncoder1());
}
});
在channel被註冊到EventLoop當中時,當前PPLine當中只有一個Handler,就是ChannelInitializer,它的channelRegistered會被調用。在它的initChannel子類回調中往PPLine中加入用戶自定義的Handler。
@Override
@SuppressWarnings("unchecked")
public final void channelRegistered(ChannelHandlerContext ctx) throws Exception {
//調用子類初始化方法,往PPline里加入Handler,該方法只會調用一次。
//在調用之前PPLine當中只有一個handler。 PPline[---this----]
//在調用之後PPLine當中加入了用戶自定義的Handler(ABCD) PPline[---this---A---B---C---D---]
if (initChannel(ctx)) {
//重新調用channelRegistered防止事件丟失。
ctx.pipeline().fireChannelRegistered();
//移除當前Handler 移除後變爲PPline[---A---B---C---D---]
removeState(ctx);
} else {
//向下傳遞事件
ctx.fireChannelRegistered();
}
}
子類回到函數,初始化PPLine,成功返回true,同時移除自己。
//子類回調成功返回true,否則返回false
private boolean initChannel(ChannelHandlerContext ctx) throws Exception {
//加入標記,保證方法只執行一次
if (initMap.add(ctx)) { // Guard against re-entrance.
try {
//子類回調函數
initChannel((C) ctx.channel());
} catch (Throwable cause) {
// Explicitly call exceptionCaught(...) as we removed the handler before calling initChannel(...).
// We do so to prevent multiple calls to initChannel(...).
exceptionCaught(ctx, cause);
} finally {
//移除當前Handler
ChannelPipeline pipeline = ctx.pipeline();
if (pipeline.context(this) != null) {
pipeline.remove(this);
}
}
return true;
}
return false;
}
移除標記位。
private void removeState(final ChannelHandlerContext ctx) {
//判斷當前ctx是否從PPLine當中移除
if (ctx.isRemoved()) {
//移除執行清空標記位
initMap.remove(ctx);
} else {
// The context is not removed yet which is most likely the case because a custom EventExecutor is used.
// Let's schedule it on the EventExecutor to give it some more time to be completed in case it is offloaded.
//否則加入EventLoop事件隊列,下一輪移除。
ctx.executor().execute(new Runnable() {
@Override
public void run() {
initMap.remove(ctx);
}
});
}
}
/**
* {@inheritDoc} If override this method ensure you call super!
*/
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
//判斷channel是否已經註冊
if (ctx.channel().isRegistered()) {
// This should always be true with our current DefaultChannelPipeline implementation.
// The good thing about calling initChannel(...) in handlerAdded(...) is that there will be no ordering
// surprises if a ChannelInitializer will add another ChannelInitializer. This is as all handlers
// will be added in the expected order.
//調用子類
if (initChannel(ctx)) {
// We are done with init the Channel, removing the initializer now.
removeState(ctx);
}
}
}