Mina源码阅读笔记(七)—Mina的拦截器FilterChain

时间:2023-03-09 15:00:09
Mina源码阅读笔记(七)—Mina的拦截器FilterChain

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,否则就在生命周期之外。

Mina源码阅读笔记(七)—Mina的拦截器FilterChain

上图仅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,然后再不断的派生开来,所以如果你要用到哪个就再自己读就行了。我觉得这部分还是注重应用为主,至少我不关心这些实现,它和通信的关系就不是很大了。