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>() {@Overrideprotected 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;
}
ChannelInitializer 的添加相关推荐
- hadoop28---netty传对象
Netty中,通讯的双方建立连接后,会把数据按照ByteBuf的方式进行传输,例如http协议中,就是通过HttpRequestDecoder对ByteBuf数据流进行处理,转换成http的对象.基于 ...
- 初学Netty(杰哥好久不见)
一.我对Netty的理解: 一个开发网络编程的框架,改善了NIO框架的缺点. 二.第一个netty小程序 1.服务器启动类ServerBootstrap:在该类中配置服务器连接数,是否延迟,长连接?添 ...
- netty中的引导Bootstrap服务端
引导一个应用程序是指对它进行配置,并使它运行起来的过程. 一.Bootstrap 类 引导类的层次结构包括一个抽象的父类和两个具体的引导子类,如图 8-1 所示 服务器致力于使用一个父 Channel ...
- Netty核心组件 ChannelPipeline和ChannelHandler与ChannelHandler的入站出站规则
概述 Netty中ChannelPipeline与Channel的对应关系是一一对应,也就是每个Channel中有且仅有一个ChannelPipeline,可以通过Channel获取唯一的Channe ...
- netty 关闭chnnal_Netty 源码学习——服务端流程分析
在上一篇我们已经介绍了客户端的流程分析,我们已经对启动已经大体上有了一定的认识,现在我们继续看对服务端的流程来看一看到底有什么区别. 服务端代码 public class NioServer { pr ...
- Netty之大动脉Pipeline
https://www.cnblogs.com/wuzhenzhao/p/11221561.html Pipeline 设计原理 Channel 与ChannelPipeline: 相信大家都已经知道 ...
- 设计模式----创建型设计模式(单例模式、工厂方法模式、构建者模式)
创建型设计模式 单例模式(Singleton Pattern) 单例模式介绍 代码演示 饿汉式(静态常量) 饿汉式(静态代码块) 懒汉式(线程不安全) 懒汉式(线程安全,同步方法) 懒汉式(线程安全, ...
- 12、Bootstrap和ServerBootstrap啥关系
一.Bootstrap.ServerBootstrap啥关系 Bootstrap客户端引导类,ServerBootstrap服务端引导类. 二.BootStrap介绍 1.客户端BootStrap B ...
- 深入分析netty(一)BootStrap与ServerBootStrap
文章目录 1.揭开BootStrap神秘面纱 1.1.客户端BootStrap 1.1.1 NioSocketChannel 的初始化过程 1.1.2 ChannelFactory 和Channel类 ...
最新文章
- Silverlight 2 beta 2 中目前不支持共享 WCF 的客户端类型
- 哪些人适合参加UI设计培训
- 使用SHA1、SHA2双证书进行微软数字签名
- TCP/IP 四次断开
- IntelliJ IDEA的配置优化
- PID算法搞不懂?看这篇文章就够了。
- python flask框架剖析_python flask框架实现传数据到js的方法分析
- Java当中捕获异常
- c语言 结构体练习之 实现产品销售记录的相关功能
- BiometricPrompt之三 - Fingerprint, Iris, Face UI优先级
- 架构师之路 — 分布式系统 — 分布式网络分区难题
- 少儿学编程系列 --- 使用python程序暴力求解:数学游戏 24 Game的答案
- Shader学习12——简易图片叠加
- idea项目配置jsp模板
- 【️C语言-游戏设置】---三子棋(N满)
- SI(crosstalk)对common path的影响(CPPR)
- 【Python大作业】耦合网络信息传播
- 河南省历年高考人数(2004-2021)
- SaaS-HRM企业管理
- 微信小程序|使用小程序制作一个马赛克处理工具