Filter我们很熟悉,在Mina中,filter chain的用法也类似于Servlet的filters,这种拦截器的设计思想能够狠轻松的帮助我们实现对资源的统一处理。我们先大致连接下mina中的filter能给我们带来什么。

  • LoggingFilter logs all events and requests.
  • ProtocolCodecFilter converts an incoming ByteBuffer into message POJO and vice versa.
  • CompressionFilter compresses all data.
  • SSLFilter adds SSL - TLS - StartTLS support.
  • and many more!

当然这中间最实用,而且源码篇幅最多的就是对codec的拦截器,这部分的应用就是可以实现自定义的编码器和解码器,并附上自定义的协议来进行通信。这部分的应用可以看:《Mina实现自定义协议的通信》

在Mina源码中,对filter的描述主要分两部分,org.apache.mina.core.filterchain以及org.apache.mina.filter.*这两部分。在核心包里主要定义了规则,然后再filter包中进行具体的实现。在讲filter的时候我们需要分清楚filter和filter chain的区别,在chain中加载的是filter,所以filter的生命周期也容易理解,加载到chain中的为active,否则就在生命周期之外。

上图仅org.apache.mina.core.filterchain内的部分关联(ReferenceCountingFilter除外),不过弄明白图上画的这些类的作用和实现原理就基本能明白整个filter的内容,后面的各种filter都是通过IoFilterAdapter派生而来。这也是我们很熟悉的适配器模式的应用。

我们先来看IoFilter,IoFilter加载到chain需经历如下过程:

1、  调用init方法,被ReferenceCountingFilter初始化(此时未加载到chain)

2、  调用onPreAdd方法,告诉filter,将要被加载到chain中去

3、  当filter被加载到chain中,filter chain开始正式起作用

4、  调用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的拦截器FilterChain的更多相关文章

  1. Mina源码阅读笔记(四)—Mina的连接IoConnector2

    接着Mina源码阅读笔记(四)-Mina的连接IoConnector1,,我们继续: AbstractIoAcceptor: 001 package org.apache.mina.core.rewr ...

  2. Mina源码阅读笔记(一)-整体解读

    今天的这一节,将从整体上对mina的源代码进行把握,网上已经有好多关于mina源码的阅读笔记,但好多都是列举了一下每个接口或者类的方法.我倒是想从mina源码的结构和功能上对这个框架进行剖析.源码的阅 ...

  3. Mina源码阅读笔记(二)- IoBuffer的封装

    在阅读IoBuffer源码之前,我们先看Mina对IoBuffer的描述:A byte buffer used by MINA applications. This is a replacement ...

  4. Mina源码阅读笔记(三)-Mina的连接IoAccpetor

    其实在mina的源码中,IoService可以总结成五部分service责任.Processor线程处理.handler处理器.接收器和连接器,分别对应着IoService.IoProcessor.I ...

  5. Mina源码阅读笔记(六)—Mina异步IO的实现IoFuture

    IoFuture是和IoSession紧密相连的一个类,在官网上并没有对它的描述,因为它一般不会显示的拿出来用,权当是一个工具类被session所使用.当然在作用上,这个系列可并不简单,我们先看源码的 ...

  6. Mina源码阅读笔记(五)—Mina对连接的操作IoSession

    IoSession是Mina管理两端的一个重要部分,也是Mina的核心,Session具有了生命周期的概念,它的生命周期和连接时紧密相关的,这点在后面的介绍中会涉及.另外,好像hibernate中也有 ...

  7. Mina源码阅读笔记(四)—Mina的连接IoConnector1

    上一篇写的是IoAcceptor是服务器端的接收代码,今天要写的是IoConnector,是客户端的连接器.在昨天,我们还留下一些问题没有解决,这些问题今天同样会产生,但是都要等到讲到session的 ...

  8. jdk源码阅读笔记-LinkedHashMap

    Map是Java collection framework 中重要的组成部分,特别是HashMap是在我们在日常的开发的过程中使用的最多的一个集合.但是遗憾的是,存放在HashMap中元素都是无序的, ...

  9. CI框架源码阅读笔记5 基准测试 BenchMark.php

    上一篇博客(CI框架源码阅读笔记4 引导文件CodeIgniter.php)中,我们已经看到:CI中核心流程的核心功能都是由不同的组件来完成的.这些组件类似于一个一个单独的模块,不同的模块完成不同的功 ...

随机推荐

  1. 开源IMDG之GridGain

    作为另一款主流的开源数据网格产品,GridGain是Hazelcast的强有力竞争者.同样提供了社区版和商业版,近日GridGain的开源版本已经进入Apache孵化器项目Ignite(一款开源的内存 ...

  2. UNIX网络编程——揭开网络编程常见API的面纱【上】

    Linux网络编程API函数初步剖析 今天我们来分析一下前几篇博文中提到的网络编程中几个核心的API,探究一下当我们调用每个API时,内核中具体做了哪些准备和初始化工作. 1.socket(famil ...

  3. Android上下文菜单ContentView详解

    ContentView介绍 上下文菜单继承了android.view.Menu,因此我们可以像操作Options Menu那样给上下文菜单增加菜单项.上下文菜单与Options Menu最大的不同在于 ...

  4. 6.5、Android Studio的Android Device Monitor

    Android Device Monitor是一个独立的工具,可以对Android应用进行调试和分析.Android Device Monitor无需安装整合在一个IDE中,比如像Android St ...

  5. Blue Path(基于cocos2dx 3.0)

    猴子原创,欢迎转载.转载请注明: 转载自Cocos2D开发网–Cocos2Dev.com,谢谢! 原文地址: http://www.cocos2dev.com/?p=498 iTunes下载:http ...

  6. 4. React 属性和状态介绍

            React 中的属性和状态初看之下可以互相替代,但是在 React 的设计哲学中两者有着截然不同的使用方式和使用场景. 属性的含义和用法         props = propert ...

  7. MySQL输入密码后闪退,这里有解

    不知道怎么的,我的MySQL就像抽风了一样,可能是不想理我了吧.只要我输入密码,它就会闪退.而且使用其他的数据库管理工具也是链接不成功的.于是下决心,调教调教它,于是有了下面的这些解决方案. 解决方法 ...

  8. Ext JS 6开发实例(三) :主界面设计

    在上文中,已经将CMD创建的应用程序导入到项目里了,而且也看到默认的主界面了,今天的主要工作就是修改这个主界面,以符合项目的需要.除了设计主界面,还有一些其他的东西需要配置一下. 添加本地化包 打开a ...

  9. 【java集合框架源码剖析系列】java源码剖析之HashMap

    前言:之所以打算写java集合框架源码剖析系列博客是因为自己反思了一下阿里内推一面的失败(估计没过,因为写此博客已距阿里巴巴一面一个星期),当时面试完之后感觉自己回答的挺好的,而且据面试官最后说的这几 ...

  10. scala学习笔记3(trait)

    // trait 类似于 Java8 中可以带 default method 的接口. // trait 中可以带有实现的方法,也可以带有抽象的方法,使用 trait 的方式是 with 而混入类中 ...