DefaultChannelPiple给出了ChannelPipleline的默认实现。ChannelPipleline是一个双向链表,本章的内容是分析默认实现中双向链表的实现。数据结构
双向列表的的数据结构ide
DefaultChannelPiple使用了三种节点类型: HeadContext, TailContext, DefaultChannelHandlerContext,这三中类型都是派生自AbstractChannelHandlerContext,这个抽象类中有双向链表所须要的两个关键属性next和prev。链表的初始化代码在构造方法中。oop
1 protected DefaultChannelPipeline(Channel channel) { 2 this.channel = ObjectUtil.checkNotNull(channel, "channel"); 3 4 tail = new TailContext(this); 5 head = new HeadContext(this); 6 7 head.next = tail; 8 tail.prev = head; 9 }
构造方法的第4-8行,时候是链表的初始化代码。HeadContext是链表头的类型,TailContext是链表尾的类型,这两个类型是DefaultChannelPiple的内部类。链表的头和尾节点是不持有channelHandler的,相比于中间节点,这两个节点比较特殊。有专门的方法用来建立中间节点,以下所示:this
1 private AbstractChannelHandlerContext newContext(EventExecutorGroup group, String name, ChannelHandler handler) { 2 return new DefaultChannelHandlerContext(this, childExecutor(group), name, handler); 3 }
添加channelHandlerspa
向链表中添加channelHandler的方法有两大类型:.net
在ChannelPiple中,每个handler是有名字的,若是用户没有给handler命名,在添加过程当中会为它生成一个不重复的名字。若是用户给handler命名重复,添加handler将会失败同时抛出异常。两种类型的添加方法最大的不一样之处在于,第1中会把新节点添加在head以后或tail以前。第2种必须先要首先找到指定名字的节点,而后把新节点添加到这个节点以后或以前。若是没找到指定名字的节点也会致使添加失败同时抛出异常。下面以addAfter为例分析添加过程。netty
1 public final ChannelPipeline addAfter( 2 EventExecutorGroup group, String baseName, String name, ChannelHandler handler) { 3 final AbstractChannelHandlerContext newCtx; 4 final AbstractChannelHandlerContext ctx; 5 6 synchronized (this) { 7 checkMultiplicity(handler); 8 name = filterName(name, handler); 9 ctx = getContextOrDie(baseName); 10 11 newCtx = newContext(group, name, handler); 12 13 addAfter0(ctx, newCtx); 14 15 EventExecutor executor = newCtx.executor(); 16 if (!executor.inEventLoop()) { 17 newCtx.setAddPending(); 18 executor.execute(new Runnable() { 19 @Override 20 public void run() { 21 callHandlerAdded0(newCtx); 22 } 23 }); 24 return this; 25 } 26 } 27 callHandlerAdded0(newCtx); 28 return this; 29 }
第8行,filterName方法,确保handler有一个名字,若是name==null, 生成一个不重复的名字。而后检查是否有重名的,若是用户指定名字重复抛出异常。code
第9行,找到baseName对应的节点,若是没有抛出异常。blog
第11行, 建立新的节点,这个节点将持有hanler,同时给这个节点分配一个eventExecutor。事件
第13行,添加链表节点的操做。
第21,27行,调用handler的handlerAdded方法,若是捕捉到异常,从链表中删除这个刚刚添加的节点,而后调用handler的handlerRemoved方法, 调用fireExceptionCaught方法触发异常事件。
其它几个添加方法几个add方法和addAfter大体相同。addBefore是把addAfter0变成了addBefore0。addFirst中没有getContextOrDie调用,把addAfter0替换陈addFirst0。addLast在addFirst的基础上把addFirst0替换成addLast0。
名字是维护链表节点的一个重要因素,DefaultChannelPipleline须要确保链表中的每一个节点的名字都重复,这样它才能经过名字找到一个惟一的节点。用户添加一个handler时,若是因为用户命名不当致使的名字重复,这个handler将会被拒绝添加的链表中。若是用户以匿名方式添加handler,添加以前DefaultChannelPipleline会为这个handler生成一个不重复的名字,这个功能在filterName方法中实现。
private String filterName(String name, ChannelHandler handler) { if (name == null) { return generateName(handler); } checkDuplicateName(name); return name; }
generateName方法负责为匿名的handler生成一个名字,checkDuplicateName负责验证用户提供的名字是否重复。名字的生成规则是handler的类型名+"#n",假设你的handler的类型名是com.test.YourClass, 那么生成名字将是YourClass#0, YourClass#1, ..., YourClass#n。
删除链表节点
全部的remove方法最终都会调用到private AbstractChannelHandlerContext remove(final AbstractChannelHandlerContext ctx)方法.
1 private AbstractChannelHandlerContext remove(final AbstractChannelHandlerContext ctx) { 2 assert ctx != head && ctx != tail; 3 4 synchronized (this) { 5 remove0(ctx); 6 7 EventExecutor executor = ctx.executor(); 8 if (!executor.inEventLoop()) { 9 executor.execute(new Runnable() { 10 @Override 11 public void run() { 12 callHandlerRemoved0(ctx); 13 } 14 }); 15 return ctx; 16 } 17 } 18 callHandlerRemoved0(ctx); 19 return ctx; 20 }
5行,从链表结构中删除handler。
12,18行, 调用handler的handlerRemoved方法。
链表节点查找
查找方法get最终都会调用内部的context0方法,这个方法是纯粹的链表操做,比较单纯。
替换链表节点
全部的replace方法最终都会调用内部的replace方法:
private ChannelHandler replace(final AbstractChannelHandlerContext ctx, final String newName, ChannelHandler newHandler)
这个方法代码结构与addAfter类似,不一样的是在链表操做上是一个替换操做,以后会先调用被替换handler的handlerRemoved方法,而后调用新handler的handlerAdded方法。
链表操做会handler方法之间的调用关系
链表方法 | ChannelHandler方法 |
addBefore,addAfter,addFirst,addLast | handlerAdded |
get | 无 |
remove,removeFirst,removeLast | handleRemoved |
replace | handleRemoved, handlerAdded |