接上一篇《異步IO實現IoFuture》 html
Filter咱們很熟悉,在Mina中,filter chain的用法也相似於Servlet的filters,這種攔截器的設計思想可以狠輕鬆的幫助咱們實現對資源的統一處理。咱們先大體鏈接下mina中的filter能給咱們帶來什麼。 java
固然這中間最實用,並且源碼篇幅最多的就是對codec的攔截器,這部分的應用就是能夠實現自定義的編碼器和解碼器,並附上自定義的協議來進行通訊。這部分的應用能夠看:《Mina實現自定義協議的通訊》 git
在Mina源碼中,對filter的描述主要分兩部分,org.apache.mina.core.filterchain以及org.apache.mina.filter.*這兩部分。在覈心包裏主要定義了規則,而後再filter包中進行具體的實現。在講filter的時候咱們須要分清楚filter和filter chain的區別,在chain中加載的是filter,因此filter的生命週期也容易理解,加載到chain中的爲active,不然就在生命週期以外。 github
上圖僅org.apache.mina.core.filterchain內的部分關聯(ReferenceCountingFilter除外),不過弄明白圖上畫的這些類的做用和實現原理就基本能明白整個filter的內容,後面的各類filter都是經過IoFilterAdapter派生而來。這也是咱們很熟悉的適配器模式的應用。 web
咱們先來看IoFilter,IoFilter加載到chain需經歷以下過程: apache
一、 調用init方法,被ReferenceCountingFilter初始化(此時未加載到chain) bootstrap
二、 調用onPreAdd方法,告訴filter,將要被加載到chain中去 設計模式
三、 當filter被加載到chain中,filter chain開始正式起做用 session
四、 調用onPostAdd方法,作一些加載完成的後處理。 數據結構
這樣的處理方式很是的經常使用,在Android中的asynctask好像常常會有相似的用法,執行前、執行中、執行後分別處理。其實也不用說那麼遠,mina中,在handler中處理session的鏈接也分session create,session open等等。在IoFilter中有一個內部接口NextFilter,用來引用在chain中的下一個filter(這個next filter會在當前的filter中被用到,因此要這麼設計)。而且,在next接口中,除了上面提到的加載到chain的方法沒有以外,其餘的都與外部類一致。
咱們再來看IoFilter的實現類DefaultIoFilter。咱們要提一下這個類,以前咱們介紹的不管是service仍是session都沒有讓開發者去重寫或者繼承,而這裏不同,因爲這個類是一個適配器的設計模式,由於必然需能夠由有大量的實現方法去充實它。因此在mina官網的user guide上給出瞭如何去繼承這個adapter,具體可見:http://mina.apache.org/mina-project/userguide/ch5-filters/ch5-filters.html
咱們能夠注意到,DefaultIoFilter的方法裏幾乎都爲空,什麼都沒有作,最多來一句:
public void sessionCreated(NextFilter nextFilter, IoSession session) throws Exception { nextFilter.sessionCreated(session); }
這樣作就一個緣由擺明了讓其餘類去作具體的實現,同時也可讓開發者本身去實現。注意,那些未實現的方法都是跟filter生命週期有關的,而receive和send這樣的方法都會由next filter來實現。
接下來咱們看IoFilterChain,這個容器裝載了咱們添加的各類攔截器,每一個session都有一個攔截器,這是一對一的關係,固然不要忘記了,攔截器的最後一步是IoHandle,這個咱們在以前就說過。咱們仍是稍稍關注一下這個接口中的內部類Entry,這是一個name-filter對,存放在chain中,比如一個map,給filter一個名字。這節最主要的就是DefaultIoFilterChain,他是整個chain能實現起來的中心,咱們主要是看mina是如何給這個filter排序的。
咱們截一段代碼,Entry的構造方法:
private EntryImpl(EntryImpl prevEntry, EntryImpl nextEntry, String name, IoFilter filter) { if (filter == null) { throw new IllegalArgumentException("filter"); } if (name == null) { throw new IllegalArgumentException("name"); } this.prevEntry = prevEntry; this.nextEntry = nextEntry; this.name = name; this.filter = filter; this.nextFilter = new NextFilter() { public void sessionCreated(IoSession session) { Entry nextEntry = EntryImpl.this.nextEntry; callNextSessionCreated(nextEntry, session); }
主要看構造方法裏的這幾個參數,後面的兩個咱們在Entry接口的時候提過,name-filter對,因此這兩個必需要,不然拋異常。那前面兩個參數從命名中咱們就能看見順序了,繼續往下看,咱們如今來看DefaultIoFilterChain(Entry是DefaultIoFilterChain的一個內部類)的構造方法:
private final Map<String, Entry> name2entry = new ConcurrentHashMap<String, Entry>(); /** The chain head */ private final EntryImpl head; /** The chain tail */ private final EntryImpl tail; public DefaultIoFilterChain(AbstractIoSession session) { if (session == null) { throw new IllegalArgumentException("session"); } this.session = session; head = new EntryImpl(null, null, "head", new HeadFilter()); tail = new EntryImpl(head, null, "tail", new TailFilter()); head.nextEntry = tail; }很明顯,這個chain在初始化的時候只有這兩個entry,一頭一尾,而且這個一頭一尾屬於惟一一個和DefaultIoFilterChain綁定的session。這個是否是很像鏈表。因此你就很容易理解AddFirst和AddLast方法是怎麼實現的了:
public synchronized void addFirst(String name, IoFilter filter) { checkAddable(name); register(head, name, filter); } private void checkAddable(String name) { if (name2entry.containsKey(name)) { throw new IllegalArgumentException("Other filter is using the same name '" + name + "'"); } } private void register(EntryImpl prevEntry, String name, IoFilter filter) { EntryImpl newEntry = new EntryImpl(prevEntry, prevEntry.nextEntry, name, filter); try { filter.onPreAdd(this, name, newEntry.getNextFilter()); } catch (Exception e) { throw new IoFilterLifeCycleException("onPreAdd(): " + name + ':' + filter + " in " + getSession(), e); } prevEntry.nextEntry.prevEntry = newEntry; prevEntry.nextEntry = newEntry; name2entry.put(name, newEntry); try { filter.onPostAdd(this, name, newEntry.getNextFilter()); } catch (Exception e) { deregister0(newEntry); throw new IoFilterLifeCycleException("onPostAdd(): " + name + ':' + filter + " in " + getSession(), e); } }
看了代碼是否是就一目瞭然了,這不就是數據結構裏咱們經常使用的雙向鏈表。因此數據結構不是白學的,軟件設計上常常會用到。這個類裏基本就是對鏈表的操做,只要對雙向鏈表的指針比較清楚的,讀懂也應該沒什麼問題。
提一下IoFilterChainBuilder和DefaultIoFilterChainBuilder。這又是一組繼承關係,DefaultIoFilterChainBuilder的實現和DefaultIoFilterChain很像,幾乎同樣。這也是可以用來作攔截器鏈的,可是它和DefaultIoFilterChain仍是有不一樣:
l DefaultIoFilterChainBuilder無論理IoFuture的生命週期
l DefaultIoFilterChainBuilder不會影響已經建立好的session
咱們來看一下IoService中的getFilterChain:
/** * A shortcut for <tt>( ( DefaultIoFilterChainBuilder ) </tt>{@link #getFilterChainBuilder()}<tt> )</tt>. * Please note that the returned object is not a <b>real</b> {@link IoFilterChain} * but a {@link DefaultIoFilterChainBuilder}. Modifying the returned builder * won't affect the existing {@link IoSession}s at all, because * {@link IoFilterChainBuilder}s affect only newly created {@link IoSession}s. * * @throws IllegalStateException if the current {@link IoFilterChainBuilder} is * not a {@link DefaultIoFilterChainBuilder} */ DefaultIoFilterChainBuilder getFilterChain();
獲得的是DefaultIoFilterChainBuilder而不是IoFilterChain。那若是你要IoFilterChain就須要用下面的方法來built:
void setFilterChainBuilder(IoFilterChainBuilder builder);
不過我看官網上也是用默認的DefaultIoFilterChainBuilder來生成chain。可能後面只要管理到handler就好了,並且通常的應用中,一條通道中也只用一個session。
Filter只講到這裏了,至於那十幾個經常使用的我以爲不必寫了,他們總有那麼一個類要去繼承IoFilterAdapter,而後再不斷的派生開來,因此若是你要用到哪一個就再本身讀就好了。我以爲這部分仍是注重應用爲主,至少我不關心這些實現,它和通訊的關係就不是很大了。
這系列的東西快結束了,明天再寫點兒雜七雜八零碎的東西,而後就結束mina這一系列的文章。感謝各位的支持了。在最後我會寫一個索引頁,把關於mina的文章都串起來方便你們閱讀。
--------------------------------------------------------------------------
昨天晚上想練練手也點兒web的東西,用了bootstrap模仿着github寫了個界面,效率還挺高的,真的要偶爾練練手,否則都忘記了。