前面我们已经分析过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 的添加相关推荐

  1. hadoop28---netty传对象

    Netty中,通讯的双方建立连接后,会把数据按照ByteBuf的方式进行传输,例如http协议中,就是通过HttpRequestDecoder对ByteBuf数据流进行处理,转换成http的对象.基于 ...

  2. 初学Netty(杰哥好久不见)

    一.我对Netty的理解: 一个开发网络编程的框架,改善了NIO框架的缺点. 二.第一个netty小程序 1.服务器启动类ServerBootstrap:在该类中配置服务器连接数,是否延迟,长连接?添 ...

  3. netty中的引导Bootstrap服务端

    引导一个应用程序是指对它进行配置,并使它运行起来的过程. 一.Bootstrap 类 引导类的层次结构包括一个抽象的父类和两个具体的引导子类,如图 8-1 所示 服务器致力于使用一个父 Channel ...

  4. Netty核心组件 ChannelPipeline和ChannelHandler与ChannelHandler的入站出站规则

    概述 Netty中ChannelPipeline与Channel的对应关系是一一对应,也就是每个Channel中有且仅有一个ChannelPipeline,可以通过Channel获取唯一的Channe ...

  5. netty 关闭chnnal_Netty 源码学习——服务端流程分析

    在上一篇我们已经介绍了客户端的流程分析,我们已经对启动已经大体上有了一定的认识,现在我们继续看对服务端的流程来看一看到底有什么区别. 服务端代码 public class NioServer { pr ...

  6. Netty之大动脉Pipeline

    https://www.cnblogs.com/wuzhenzhao/p/11221561.html Pipeline 设计原理 Channel 与ChannelPipeline: 相信大家都已经知道 ...

  7. 设计模式----创建型设计模式(单例模式、工厂方法模式、构建者模式)

    创建型设计模式 单例模式(Singleton Pattern) 单例模式介绍 代码演示 饿汉式(静态常量) 饿汉式(静态代码块) 懒汉式(线程不安全) 懒汉式(线程安全,同步方法) 懒汉式(线程安全, ...

  8. 12、Bootstrap和ServerBootstrap啥关系

    一.Bootstrap.ServerBootstrap啥关系 Bootstrap客户端引导类,ServerBootstrap服务端引导类. 二.BootStrap介绍 1.客户端BootStrap B ...

  9. 深入分析netty(一)BootStrap与ServerBootStrap

    文章目录 1.揭开BootStrap神秘面纱 1.1.客户端BootStrap 1.1.1 NioSocketChannel 的初始化过程 1.1.2 ChannelFactory 和Channel类 ...

最新文章

  1. Silverlight 2 beta 2 中目前不支持共享 WCF 的客户端类型
  2. 哪些人适合参加UI设计培训
  3. 使用SHA1、SHA2双证书进行微软数字签名
  4. TCP/IP 四次断开
  5. IntelliJ IDEA的配置优化
  6. PID算法搞不懂?看这篇文章就够了。
  7. python flask框架剖析_python flask框架实现传数据到js的方法分析
  8. Java当中捕获异常
  9. c语言 结构体练习之 实现产品销售记录的相关功能
  10. BiometricPrompt之三 - Fingerprint, Iris, Face UI优先级
  11. 架构师之路 — 分布式系统 — 分布式网络分区难题
  12. 少儿学编程系列 --- 使用python程序暴力求解:数学游戏 24 Game的答案
  13. Shader学习12——简易图片叠加
  14. idea项目配置jsp模板
  15. 【️C语言-游戏设置】---三子棋(N满)
  16. SI(crosstalk)对common path的影响(CPPR)
  17. 【Python大作业】耦合网络信息传播
  18. 河南省历年高考人数(2004-2021)
  19. SaaS-HRM企业管理
  20. 微信小程序|使用小程序制作一个马赛克处理工具

热门文章

  1. ASP.NET+SQL创建存储过程
  2. maven如何将本地jar安装到本地仓库
  3. 团队-象棋游戏-需求分析
  4. spring @Value注解#和$区别
  5. 浏览器插件-- Browser Helper Object(BHO) 一
  6. delphi7下调用微软的Web Services的心得
  7. zabbix—自动发现端口并监控
  8. 最短编辑距离问题理解
  9. Re: 从零开始的【comic spider】(序幕)
  10. Service 深度解析