0. ChannelInitializer簡介
直接用ChannelInitializer的注釋吧:A special ChannelInboundHandler which offers an easy way to initialize a Channel once it was registered to its eventLoop.
1. ChannelInitializer類圖
需要注意的是:
a. ChannelInitializer繼承於ChannelInboundHandler接口
b. ChannelInitializer是一個抽象類,不能直接使用
2. initChannel抽象方法
ChannelInitializer中聲明了一個名為initChannel的抽象方法:
/**
* This method will be called once the {@link Channel} was registered. After the method returns this instance
* will be removed from the {@link ChannelPipeline} of the {@link Channel}.
*
* @param ch the {@link Channel} which was registered.
* @throws Exception is thrown if an error occurs. In that case it will be handled by
* {@link #exceptionCaught(ChannelHandlerContext, Throwable)} which will by default close
* the {@link Channel}.
*/
protected abstract void initChannel(C ch) throws Exception;
ChannelInitializer的實現類必須要重寫這個方法,這個方法在Channel被注冊到EventLoop的時候會被調用
3. ChannelInitializer什么時候會被調用?
以ServerBootstrap啟動這一場景為例
在ServerBootstrap.init()方法中,負責accept新鏈接的Channel的pipeline被添加了一個ChannelInitializer
p.addLast(new ChannelInitializer<Channel>() {
@Override
public void initChannel(final Channel ch) throws Exception {
final ChannelPipeline pipeline = ch.pipeline();
ChannelHandler handler = config.handler();
if (handler != null) {
pipeline.addLast(handler);
}
ch.eventLoop().execute(new Runnable() {
@Override
public void run() {
pipeline.addLast(new ServerBootstrapAcceptor(
ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
}
});
由於此時這個Channel還沒有被register到EventLoop,於是在addLast方法的調用鏈中,會給pipeline添加一個PendingHandlerAddedTask,其目的是在Channel被register到EventLoop的時候,觸發一個回調事件
然后在AbstractBootstrap.initAndRegister()方法中,這個Channel會被register到boss EventLoopGoup,接着會被register到boss EventLoopGoup中的某一個具體的EventLoop
在AbstractChannel.register0()方法中,之前注冊的PendingHandlerAddedTask會被調用,經過一系列調用之后,ChannelInitializer.handleAdded()方法會被觸發:
/** * {@inheritDoc} If override this method ensure you call super! */ @Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { 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. initChannel(ctx); } } @SuppressWarnings("unchecked") private boolean initChannel(ChannelHandlerContext ctx) throws Exception { if (initMap.putIfAbsent(ctx, Boolean.TRUE) == null) { // Guard against re-entrance. try { initChannel((C) ctx.channel());//調用子類重寫的initChannel方法 } 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 { remove(ctx);//將ChannelInitializer從pipeline中移除 } return true; } return false; } /** * This method will be called once the {@link Channel} was registered. After the method returns this instance * will be removed from the {@link ChannelPipeline} of the {@link Channel}. * * @param ch the {@link Channel} which was registered. * @throws Exception is thrown if an error occurs. In that case it will be handled by * {@link #exceptionCaught(ChannelHandlerContext, Throwable)} which will by default close * the {@link Channel}. */ protected abstract void initChannel(C ch) throws Exception; private void remove(ChannelHandlerContext ctx) { try { ChannelPipeline pipeline = ctx.pipeline(); if (pipeline.context(this) != null) { pipeline.remove(this); } } finally { initMap.remove(ctx); } }
大概意思是:
a. 觸發ChannelInitializer的initChannel方法,執行子類定義的一系列操作(在ServerBootstrap這個例子中就是將ServerBootstrapAcceptor注冊到pipeline中)
b. 將ChannelInitializer從pipeline中移除
4. 總結
ChannelInitializer的主要目的是為程序員提供了一個簡單的工具,用於在某個Channel注冊到EventLoop后,對這個Channel執行一些初始化操作。ChannelInitializer雖然會在一開始會被注冊到Channel相關的pipeline里,但是在初始化完成之后,ChannelInitializer會將自己從pipeline中移除,不會影響后續的操作。
使用場景:
a. 在ServerBootstrap初始化時,為監聽端口accept事件的Channel添加ServerBootstrapAcceptor
b. 在有新鏈接進入時,為監聽客戶端read/write事件的Channel添加用戶自定義的ChannelHandler