ChannelInitializer 的添加

前面我们已经分析过Channel 的组成,其中我们了解到,最开始的时候ChannelPipeline 中含有两个ChannelHandlerContext(同时也是ChannelHandler),但是这个Pipeline 并不能实现什么特殊的功能,因为我们还没有给它添加自定义的ChannelHandler。通常来说,我们在初始化Bootstrap,会添加我们自定义的ChannelHandler,就以我们具体的客户端启动代码片段来举例:

Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)
.channel(NioSocketChannel.class)
.option(ChannelOption.SO_KEEPALIVE, true)
.handler(new ChannelInitializer<SocketChannel>() {
	@Override
	protected void initChannel(SocketChannel ch) throws Exception {
	ChannelPipeline pipeline = ch.pipeline();
	pipeline.addLast(new ChatClientHandler(nickName));
	}
});

上面代码的初始化过程,相信大家都不陌生。在调用handler 时,传入了ChannelInitializer 对象,它提供了一个initChannel()方法给我我们初始化ChannelHandler。那么这个初始化过程是怎样的呢?下面我们来揭开它的神秘面纱。ChannelInitializer 实现了ChannelHandler,那么它是在什么时候添加到ChannelPipeline 中的呢?通过代码跟踪,我们发现它是在Bootstrap 的init()方法中添加到ChannelPipeline 中的,其代码如下:

void init(Channel channel) throws Exception {
	ChannelPipeline p = channel.pipeline();
	p.addLast(config.handler());
	//略去N 句代码
}

从上面的代码可见,将handler()返回的ChannelHandler 添加到Pipeline 中,而handler()返回的其实就是我们在初始化Bootstrap 时通过handler()方法设置的ChannelInitializer 实例,因此这里就是将ChannelInitializer 插入到了Pipeline的末端。此时Pipeline 的结构如下图所示:

这时候,有小伙伴可能就有疑惑了,我明明插入的是一个ChannelInitializer 实例,为什么在ChannelPipeline 中的双向链表中的元素却是一个ChannelHandlerContext 呢?我们继续去源码中寻找答案。

刚才,我们提到,在Bootstrap 的init()方法中会调用p.addLast()方法,将ChannelInitializer 插入到链表的末端:

public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
final AbstractChannelHandlerContext newCtx;
synchronized (this) {
	checkMultiplicity(handler);
	newCtx = newContext(group, filterName(name, handler), handler);
	addLast0(newCtx);
	// 略去N 句代码
	return this;
}
private AbstractChannelHandlerContext newContext(EventExecutorGroup group, String name, ChannelHandler handler) {
	return new DefaultChannelHandlerContext(this, childExecutor(group), name, handler);
}

addLast()有很多重载的方法,我们只需关注这个比较重要的方法就行。上面的addLast()方法中,首先检查ChannelHandler 的名字是否是重复,如果不重复,则调用newContex()方法为这个Handler 创建一个对应的DefaultChannelHandlerContext 实例,并与之关联起来(Context 中有一个handler 属性保存着对应的Handler 实例)。为了添加一个handler 到pipeline 中,必须把此handler 包装成ChannelHandlerContext。因此在上面的代码中我们可以看到新实例化了一个newCtx 对象,并将handler 作为参数传递到构造方法中。那么我们来看一下实例化的
DefaultChannelHandlerContext 到底有什么玄机吧。首先看它的构造器:

DefaultChannelHandlerContext(DefaultChannelPipeline pipeline, EventExecutor executor, String name, ChannelHandler handler) {
	super(pipeline, executor, name, isInbound(handler), isOutbound(handler));
	if (handler == null) {
		throw new NullPointerException("handler");
	}
	this.handler = handler;
}

在DefaultChannelHandlerContext 的构造器中,调用了两个很有意思的方法:isInbound()与isOutbound(),这两个方法是做什么的呢?

private static boolean isInbound(ChannelHandler handler) {
	return handler instanceof ChannelInboundHandler;
}
private static boolean isOutbound(ChannelHandler handler) {
	return handler instanceof ChannelOutboundHandler;
}

从源码中可以看到,当一个handler 实现了ChannelInboundHandler 接口,则isInbound 返回true;类似地,当一个handler 实现了ChannelOutboundHandler 接口,则isOutbound 就返回true。而这两个boolean 变量会传递到父类AbstractChannelHandlerContext 中,并初始化父类的两个字段:inbound 与outbound。

那么这里的ChannelInitializer 所对应的DefaultChannelHandlerContext 的inbound 与inbound 字段分别是什么呢? 那就看一下ChannelInitializer 到底实现了哪个接口不就行了?如下是ChannelInitializer 的类层次结构图:

从类图中可以清楚地看到,ChannelInitializer 仅仅实现了ChannelInboundHandler 接口,因此这里实例化的DefaultChannelHandlerContext 的inbound = true,outbound = false。

兜了一圈,不就是inbound 和outbound 两个字段嘛,为什么需要这么大费周折地分析一番?其实这两个字段关系到pipeline 的事件的流向与分类,因此是十分关键的,不过我在这里先卖个关子, 后面我们再来详细分析这两个字段所起的作用。至此, 我们暂时先记住一个结论:ChannelInitializer 所对应的DefaultChannelHandlerContext 的inbound = true,outbound = false。

当创建好Context 之后,就将这个Context 插入到Pipeline 的双向链表中,基础较差的可以将下面的逻辑用图画出来:

private void addLast0(AbstractChannelHandlerContext newCtx) {
	AbstractChannelHandlerContext prev = tail.prev;
	newCtx.prev = prev;
	newCtx.next = tail;
	prev.next = newCtx;
	tail.prev = newCtx;
}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值