Mina中的过滤器处于IoService与IoHandler之间,用于过滤每一个I/O事件。本文分析Mina中的过滤器是怎么串起来的?

前面提到了IoFilter,FilterChain等接口和类,在分析过滤器链怎么串起来之前,有必要看一下这些接口和类之间的关系。

如上图所示:

FilterChain是由一个个Entry串起来的,EntryImpl是Entry的实现;

从EntryImpl中可以获取到Filter与NextFilter,NextFilter相当于那根线(指针);

有两个特殊的Entry,里面的Filter分别是HeadFilter和TailFilter,我们添加的Filter都处于这两个Filter之间;

每做完一个Entry,NextFilter会回到FilterChain处理下一个Entry(处理顺序如下图所示)。

由上图可知:

(1)当消息到来时,触发过滤器链的fireXXX事件(这一步一般在processor里面触发);

(2)从过滤器链获取上头结点Entry,从头结点Entry中取出filter和nextFilter;

(3)Filter处理后,交由nextFilter处理,nextFilter并不是一个真正的Filter,它决定这个过滤器链的走向,在这里它是返回到过滤器链;

(4)过滤器链通过nextFilter指针得到下一个entry,重复执行(2)、(3),直到每个Entry都处理完。

这里抛出两个问题:

1、如何保证新加入的过滤器在HeadFilter与TailFilter之间?

2、nextFilter一定是从左向右的顺序吗?

3、TailFilter调用了IoHandler吗?

对于第一个问题,答案在EntryImpl的构造方法里面

// EntryImpl构造方法
private EntryImpl(EntryImpl prevEntry, EntryImpl nextEntry, String name, IoFilter filter) {
......
this.prevEntry = prevEntry;
this.nextEntry = nextEntry;
this.name = name;
this.filter = filter;
this.nextFilter = new NextFilter() {
......
};
} //FilterChain构造方法
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;
}

在FilterChain构造方法里面初始化了两个Entry:

我们一般调用IoFilterChain的如下方法添加过滤器

void addFirst(String name, IoFilter filter);
void addLast(String name, IoFilter filter);
void addBefore(String baseName, String name, IoFilter filter);
void addAfter(String baseName, String name, IoFilter filter);

这里以addLast为例,先看看addLast方法的实现

public synchronized void addFirst(String name, IoFilter filter) {
checkAddable(name);
register(head, name, filter);
} private void register(EntryImpl prevEntry, String name, IoFilter filter) {
EntryImpl newEntry = new EntryImpl(prevEntry, prevEntry.nextEntry, name, filter);
......
prevEntry.nextEntry.prevEntry = newEntry;
prevEntry.nextEntry = newEntry;
name2entry.put(name, newEntry);
......
}

从代码一目了然,这就是一个简单的链表操作,在这里第一个问题回答完毕。

对于第二个问题答案是否定的。从IoFilter接口可以看出,一共处理以下几种事件:

sessionCreated
sessionOpened
sessionClosed
sessionIdle
exceptionCaught
messageReceived
messageSent
filterClose
filterWrite
前面FilterChain的构造方法中,省略了NextFilter的实现,这里补上

 this.nextFilter = new NextFilter() {
public void sessionCreated(IoSession session) {
Entry nextEntry = EntryImpl.this.nextEntry;
callNextSessionCreated(nextEntry, session);
} public void sessionOpened(IoSession session) {
Entry nextEntry = EntryImpl.this.nextEntry;
callNextSessionOpened(nextEntry, session);
} public void sessionClosed(IoSession session) {
Entry nextEntry = EntryImpl.this.nextEntry;
callNextSessionClosed(nextEntry, session);
} public void sessionIdle(IoSession session, IdleStatus status) {
Entry nextEntry = EntryImpl.this.nextEntry;
callNextSessionIdle(nextEntry, session, status);
} public void exceptionCaught(IoSession session, Throwable cause) {
Entry nextEntry = EntryImpl.this.nextEntry;
callNextExceptionCaught(nextEntry, session, cause);
} public void messageReceived(IoSession session, Object message) {
Entry nextEntry = EntryImpl.this.nextEntry;
callNextMessageReceived(nextEntry, session, message);
} public void messageSent(IoSession session, WriteRequest writeRequest) {
Entry nextEntry = EntryImpl.this.nextEntry;
callNextMessageSent(nextEntry, session, writeRequest);
} public void filterWrite(IoSession session, WriteRequest writeRequest) {
Entry nextEntry = EntryImpl.this.prevEntry;
callPreviousFilterWrite(nextEntry, session, writeRequest);
} public void filterClose(IoSession session) {
Entry nextEntry = EntryImpl.this.prevEntry;
callPreviousFilterClose(nextEntry, session);
}
}

可以看出filterWrite与filterClose是反向的,其他都是正向的。

对于第三个问题,直接看TailFilter的代码即可:

public void messageReceived(NextFilter nextFilter, IoSession session, Object message) throws Exception {
AbstractIoSession s = (AbstractIoSession) session;
if (!(message instanceof IoBuffer)) {
s.increaseReadMessages(System.currentTimeMillis());
} else if (!((IoBuffer) message).hasRemaining()) {
s.increaseReadMessages(System.currentTimeMillis());
} try {
session.getHandler().messageReceived(s, message);
} finally {
if (s.getConfig().isUseReadOperation()) {
s.offerReadFuture(message);
}
}
}

确实调用了handler来处理真正的请求。

总结一下:FilterChain是由Entry组成一个链表,HeadFilter与TailFilter所在Entry分别是链表的首尾,HeadFilter与processor相连,TailFilter与handler相连,添加Filter的操作实际上是普通的链表插入操作。FilterChain可以触发多种事件,每种事件到来时Filter的顺序是正序还是倒序由nextFilter决定。

Apache Mina Filter的更多相关文章

  1. Apache MiNa 实现多人聊天室

    Apache MiNa 实现多人聊天室 开发环境: System:Windows JavaSDK:1.6 IDE:eclipse.MyEclipse 6.6 开发依赖库: Jdk1.4+.mina-c ...

  2. Apache Mina(一)

    原文链接:http://www.cnblogs.com/xuekyo/archive/2013/03/06/2945826.html Apache Mina是一个能够帮助用户开发高性能和高伸缩性网络应 ...

  3. Apache Mina 入门实例

    这个教程是介绍使用Mina搭建基础示例.这个教程内容是以创建一个时间服务器. 以下是这个教程需要准备的东西: MINA 2.0.7 Core JDK 1.5 或更高 SLF4J 1.3.0 或更高 L ...

  4. Apache Mina原理及典型例子分析

    Apache Mina ,一个高性能 Java 异步并发网络通讯框架.利用 Mina 可以高效地完成以下任务: TCP/IP 和 UDP/IP 通讯 串口通讯 VM 间的管道通讯 SSL/TLS JX ...

  5. 网络通信框架Apache MINA

    Apache MINA(Multipurpose Infrastructure for Network Applications) 是 Apache 组织一个较新的项目,它为开发高性能和高可用性的网络 ...

  6. Apache Mina入门实例

    一.mina是啥 ApacheMINA是一个网络应用程序框架,用来帮助用户简单地开发高性能和高可扩展性的网络应用程序.它提供了一个通过Java NIO在不同的传输例如TCP/IP和UDP/IP上抽象的 ...

  7. Apache Mina -2

    我们可以了解到 mina是个异步通信框架,一般使用场景是服务端开发,长连接.异步通信使用mina是及其方便的.不多说,看例子. 本次mina 使用的例子是使用maven构建的,过程中需要用到的jar包 ...

  8. Apache Mina UDP连接目标服务器地址时出现异常

    俩种情形,第一种是开始连接时候就没连上服务器:第二种是服务器关闭连接,出现的异常: 第一种: java.lang.reflect.InvocationTargetException at sun.re ...

  9. Apache Mina入门

    Mina第一次听到这个名称的时候,我以为是个MM的名字米娜,后来才知道… Apache MINA(Multipurpose Infrastructure for Network Application ...

随机推荐

  1. ViewGroup.layout(int l, int t, int r, int b)四个输入参数的含义

    ViewGroup.layout(int l, int t, int r, int b)这个方法是确定View的大小和位置的,然后将其绘制出来,里面的四个参数分别是View的四个点的坐标,他的坐标不是 ...

  2. poj1521

    霍夫曼编码,建树 #include <cstdio> #include <cstring> #include <queue> using namespace std ...

  3. jquery-扩展

    jQuery扩展三种方式:$.extend,$.fn.extend,外部文件. 1)jQuery.extend(object) 调用   $.方法 2)jQuery.fn.extend(object) ...

  4. PHP 字符串截取()[]{} 中内容

    $str="你好<我>(爱)[北京]{天安门}"; echo f1($str); //返回你好 echo f2($str); //返回我 echo f3($str); ...

  5. C#用Oracle.DataAccess中连接Oracle要注意版本问题!

    客户端Oracle.DataAccess.dll与服务器版本不一致时,如下修改:1.在客户端Web.config中,增加如下配置:<runtime> <assemblyBinding ...

  6. K-Means和K Nearest Neighbor

    来自酷壳: http://coolshell.cn/articles/7779.html http://coolshell.cn/articles/8052.html

  7. php返回上一页

    echo "<script>alert('没有获取到订单信息');history.go(-1);</script>";

  8. 升级到AndroidStudio3.2.1的注意事项

    升级到AndroidStudio3.2.1的注意事项     即不用再在二级结构的build.gradle中的compileSdkVersion 28 下声明 buildToolsVersion &q ...

  9. mysql 事务隔离级别 详解

    问题 在工作中真实遇到的问题:用python连接mysql,查询数据,同时有别的代码在更新mysql中的数据,前者是一直是保持连接的数据库,每一分钟select一次,但第二次却查不到更新后的数据?wh ...

  10. 【python学习-2】python起步必备

    1.python缩进 python 缩进是tab,还是空格呢?都可以,可以是一个tab,也可以是4个空格,但是最重要的是整个python脚本的缩进必须统一,否则会报错. 2.代码注释 python注释 ...