ChannelHandlerContext,ChannelInitializer组件的理解与笔记
我们今天来看看ChannelInitializer组件
ChannelInitializer这个组件我们可以理解为Channel的便捷对象,在我提供的程序中,也用到了这个类,我们来看看他到底起的是什么样的作用呢?
我们看到我们自己写的MyServerInitializer 这个类是继承自它的,接着我们看看它的Doc与,描述看看与这个类的作用
public class MyServerInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
//获取到它的pipeline对象并且吧我们需要的对应的编码器添加到pipeline对象中
ChannelPipeline pipeline = ch.pipeline();
//添加一些默认的编码器与解码器
pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4));
pipeline.addLast(new LengthFieldPrepender(4));
pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));
pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));
//我们自己所编写的业务逻辑Handle
pipeline.addLast(new MyServerHandler());
}
}
ChannelInitializer类图
需要注意的是:
a. ChannelInitializer继承于ChannelInboundHandler接口
b. ChannelInitializer是一个抽象类,不能直接使用
- 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的时候会被调用
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中移除
总结
ChannelInitializer的主要目的是为程序员提供了一个简单的工具,用于在某个Channel注册到EventLoop后,对这个Channel执行一些初始化操作。ChannelInitializer虽然会在一开始会被注册到Channel相关的pipeline里,但是在初始化完成之后,ChannelInitializer会将自己从pipeline中移除,不会影响后续的操作。
使用场景:
a. 在ServerBootstrap初始化时,为监听端口accept事件的Channel添加ServerBootstrapAcceptor
b. 在有新链接进入时,为监听客户端read/write事件的Channel添加用户自定义的ChannelHandler
接着我们看看 ChannelHandlerContext这个对象
a 这个组件可以理解为ChannelHandler和ChannelPipeline之间的桥梁
b 它可以让ChannelHandler与ChannelPipeline以及其他handler交互,此外一个context还可以通知下一个handle,也可以修改pipeline中的对象
c 你可以通过获取到pipeline之后,获取到context进行相关对象的操作,可以动态的去操作
如它的里面的Doc来说:
可以提前把ChannelHandlerContext 保存起来供后面使用
例如*在处理程序方法之外触发事件,甚至可以从不同的线程触发。
//创建一个MyHandler 并且继承 ChannelDuplexHandle
public class MyHandler extends {@link ChannelDuplexHandler} {
* //提前获取到一个context对象 通过构造函数赋值
* private ChannelHandlerContext ctx ;
*
*
* public void beforeAdd({@link ChannelHandlerContext} ctx) {
* <b>this.ctx = ctx;</b>
* }
* public void login(String username, password) {
* //在在另外的业务逻辑当中处理别的业务.
* //在其他地方操作
* ctx.write(new LoginMessage(username, password));
* }
* ...
* }
ChannelHandlerContext特点
1 通常情况下一个Handle有一个Context对象与之对应 ,但是也允许一个Handle可以有多个Context对象
2 一个pipeline可以多次添加,同一个Handler对象,并且,每添加一次就会有之对应的Context对应自动生成.
3 ChannelHandlerContext 是连接两个组件之间的桥梁
问题 pipeline 本身是一个存放Handler容器, 那pipeline与Context之间的关系是什么?
a , pipeline本身是一个与Channel所关联的容器对象.存放的是ChannelHandle对象
b ,ChannelHandlerContext – >是Netty自动生成的, 是 ChannelHandle 与 pipeline之间的一个纽带
c ,将Handle 关联到Netty创建的Context当中 并且吧Context 加入到pipeline 当中
d ,用 ChannelInitializer 一次性添加的若干个对象,并且添加进去,我们自己手写的那个也就是Myinit()的方法,添加完成之后便会删除该 ChannelInitializer对象
e ,实例中虽然添加的是Handler对象.但是Netty会自动的会吧该handle生成context对象并且把,它添加到pipeline当中