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 |