Mina中的过滤器,位于ioService和ioHandler之间,用于在正式的业务处理之前,做一些额外的预处理或者过滤。比如,编解码过滤器,日志过滤器,心跳过滤器等。下图是mina官网上的一个mina应用的结构图:

多个过滤器共同组成过滤器链,这篇文章对mina中的过滤器的自定义、如何进入和退出过滤器链、过滤器之间的传递、过滤器链中过滤器的顺序问题等做一个研究。

mina中默认使用的过滤器链是DefaultIoFilterChain,对于过滤器和过滤器链,基本上所有的需要了解的关键都在这个类里面。

1. 自定义过滤器。

这个问题不是本篇的重点,简单说一下就行。比如,我们需要在mina应用中插入日志功能,mina中有一个现在的LoggingFilter,插入即可:

DefaultIoFilterChain.addLast(new LoggingFilter())

2. 如何进入过滤器链

比如说,服务端要写数据到客户端,流程是什么时候进入过滤器链的?

要发送数据,首先在handler里面调用iosession.write(msg), 稍微一跟踪,就会发现,write方法中,调用了过滤器链的fireFilterWrite()方法:

  filterChain.fireFilterWrite(writeRequest);

这就进入了过滤器链,然后接着跟踪,最终会按顺序调用一个个过滤器的filterWrite()方法:

  filter.filterWrite(nextFilter, session, writeRequest);

在查看DefaultIoFilterChain类中的源码,fire开头的方法很多,如fireMessageReceived、fireSessionClosed,。。。这些方法的意思也很明显,触发相关的事件。

3. 如何退出过滤器链

现在考虑一个问题,一般的过滤器,他们的事件处理方法中,处理完之后,一般都会调用下一个过滤器的处理方法,如:LoggingFilter中messageReceived方法:

    @Overridepublic void messageReceived(NextFilter nextFilter, IoSession session, Object message) throws Exception {log(messageReceivedLevel, "RECEIVED: {}", message);nextFilter.messageReceived(session, message);}

我们知道,所有过滤器都处理完了之后,messageReceived会转交给IoHandler处理,这件事应该是最后一个过滤器来做,但是自己的业务代码中从来没有写过把消息转交给handler的工作。猜测:DefaultIoFilterChain中应该有一个最后把关的过滤器,来做这个事情,所以翻开代码继续查看:

DefaultIoFilterChain有一个head和tail,构造DefaultIoFilterChain时,对这两个成员进行了初始化:

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;
}

head和tail是一个EntryImpl,里面包含filter。从这里看出head包含一个HeadFilter,tail包含一个TailFilter。

根据猜测,TailFilter是最后一个filter,那么我们看看他的messageReceived是怎么写的:

session.getHandler().messageReceived(s, message);

大部分省略,上面这句是重点,调用了handler的方法,猜测得到验证。

配合DefaultIoFilterChain的addLast、addFirst等方法,我们发现,不管我们往过滤器链中插入了多少过滤器,head始终作为第一个节点,而tail始终作为最后一个。

至于head有什么作为,下面会讲。

4. 过滤器的传递方向问题

继续说write,写数据进入过滤器链中,调用fireFilterWrite方法,在这个方法中调用了另一个方法:callPreviousFilterWrite,这个方法有点奇怪,调用前一个filterWrite方法?

在其他的fire方法中,调用的是next,比如fireMessageReceived中,调用了callNextMessageReceived,调用下一个的messageReceived方法。那为什么在fireFilterWrite 中,要调用前一个的filterWrite方法呢?方法的逻辑是否真和方法名所表明的一样呢?

public void fireFilterWrite(WriteRequest writeRequest) {callPreviousFilterWrite(tail, session, writeRequest);}private void callPreviousFilterWrite(Entry entry, IoSession session, WriteRequest writeRequest) {try {IoFilter filter = entry.getFilter();NextFilter nextFilter = entry.getNextFilter();filter.filterWrite(nextFilter, session, writeRequest);} catch (Exception e) {writeRequest.getFuture().setException(e);fireExceptionCaught(e);} catch (Error e) {writeRequest.getFuture().setException(e);fireExceptionCaught(e);throw e;}}

查看源码发现,fireFilterWrite方法确实是从tail开始的,首先调用了tail中的filter的filterWrite方法,跟踪发现Tail的filterWrite方法中又调用了他的下一个filter的filterWrite:

@Override
public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws Exception {nextFilter.filterWrite(session, writeRequest);
}

不仅如此,其他继承了IoFilterAdapter的普通filter,也都是调用了nextFilter.filterWrite(session, writeRequest); 既然调用的是nextFilter,为什么叫callPreviousFilterWrite?

在创建每个过滤器entry的时候,构造了他的nextFilter,我们发现,每个过滤器的构造,都是一个EntryImpl对象,我们看看这个对象的源码,构造方法中有这么一段:

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 inputClosed(IoSession session) {Entry nextEntry = EntryImpl.this.nextEntry;callNextInputClosed(nextEntry, session);}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);}public String toString() {return EntryImpl.this.nextEntry.name;}};

这里定义了nextFilter, 注意这里面filterWrite方法和 filterClose方法的不同:

Entry nextEntry = EntryImpl.this.prevEntry;
callPreviousFilterWrite(nextEntry, session, writeRequest);

把prevEntry赋给了nextENtry, 与messageReceived刚好相反,这样就一清二楚了,所谓的nextEntry其实是prevEntry。那么我们继续猜测,在写的时候,head将会是最后一个过滤器,负责把消息交给下层的ioProcessor处理,看看HeadFilter的源码:

private class HeadFilter extends IoFilterAdapter {@SuppressWarnings("unchecked")@Overridepublic void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws Exception {AbstractIoSession s = (AbstractIoSession) session;// Maintain counters.if (writeRequest.getMessage() instanceof IoBuffer) {IoBuffer buffer = (IoBuffer) writeRequest.getMessage();// I/O processor implementation will call buffer.reset()// it after the write operation is finished, because// the buffer will be specified with messageSent event.buffer.mark();int remaining = buffer.remaining();if (remaining > 0) {s.increaseScheduledWriteBytes(remaining);}} else {s.increaseScheduledWriteMessages();}WriteRequestQueue writeRequestQueue = s.getWriteRequestQueue();if (!s.isWriteSuspended()) {if (writeRequestQueue.isEmpty(session)) {// We can write directly the messages.getProcessor().write(s, writeRequest);} else {s.getWriteRequestQueue().offer(s, writeRequest);s.getProcessor().flush(s);}} else {s.getWriteRequestQueue().offer(s, writeRequest);}}@SuppressWarnings("unchecked")@Overridepublic void filterClose(NextFilter nextFilter, IoSession session) throws Exception {((AbstractIoSession) session).getProcessor().remove(session);}}

重点是下面这句:

if (writeRequestQueue.isEmpty(session)) {// We can write directly the messages.getProcessor().write(s, writeRequest);
} else {s.getWriteRequestQueue().offer(s, writeRequest);s.getProcessor().flush(s);
}

再次得到证实。

总结:

我们发现,NextFilter中,只有filterClose方法和filterWrite相同,把prevEntry作为nextEntry。

这种现象的本质是,写和连接关闭的事件,在过滤器链中的传递方向,与其他事件的传递方向是刚好相反的,这是为什么?

上图F1、F2、F3是过滤器链中的三个过滤器,事件流的方向有A和B两种方向,服务端向客户端写数据,是一种handler主动的操作,在handler中调用session的write方法,所以事件流是A方向,而当有消息收到时,事件流的方向是B,其他类似。

再假设,F1和F2是两个不同的编解码器,当有消息来时,首先经过F2解码,再经过F1解码,最后交给handler处理。那么,当要发送消息的时候,要对消息进行编码,是不是应该先经过F1编码,然后才让F2编码,如果反了,客户端解码就得不到正确结果。当然,这样做是基于服务端和客户端具有完全相同的过滤器链(包括过滤器顺序也要相同), 这是一个基本原则。

Mina过滤器链原理相关推荐

  1. Java过滤器链原理解析

    在很多Java Web项目中我们会在web.xml中配置一些过滤器来拦截请求,比如下面解决乱码的编码过滤器: <filter><filter-name>encodingFilt ...

  2. SpringSecurity过滤器链加载原理

    SpringSecurity过滤器链加载原理 通过前面十五个过滤器功能的介绍,对于SpringSecurity简单入门中的疑惑是不是在心中已经有了答案了呀? 但新的问题来了!我们并没有在web.xml ...

  3. Servlet→对象监听器、事件监听器、Session钝化活化、@WebListener标注、过滤器概念原理生命周期、过滤器链、@WebFilter标注、定时器Timer、cancel()、purge

    监听器ServletContextListener HttpSessionListener ServletRequestListener 事件监听器 Session钝化活化 @WebListener标 ...

  4. Dubbo之Filter链原理

    本文来说下Dubbo的Filter链原理 文章目录 概述 构造Filter链 Consumer ConsumerContextFilter ActiveLimitFilter FutureFilter ...

  5. 安全认证框架Shiro (二)- shiro过滤器工作原理

    安全认证框架Shiro (二)- shiro过滤器工作原理 安全认证框架Shiro 二- shiro过滤器工作原理 第一前言 第二ShiroFilterFactoryBean入口 第三请求到来解析过程 ...

  6. Spring Security之过滤器链【探案】+源码剖析

    Spring Security之过滤器链[探案] Spring Security常用过滤器介绍 过滤器是一种典型的AOP思想,关于什么是过滤器,就不赘述了,接下来咱们就一起看看Spring Secur ...

  7. 关于vue过滤器的原理解析

    又来学习源码系列,今天就看一下vue中的过滤器具体是怎么实现的,我觉得这是一个不常用但是很重要的知识点,开冲! 01 前言 过滤器实质不改变原始数据,只是对数据进行加工处理后返回过滤后的数据再进行调用 ...

  8. Java Servlet中Filter过滤器的原理以及使用方式

    详细介绍了Java Web Servlet中的Filter过滤器的原理以及常见用法. 文章目录 1 Filter接口 1.1 过滤器的生命周期 1.2 doFilter过滤方法 2 Filter的使用 ...

  9. 算法:详解布隆过滤器的原理、使用场景和注意事项@知乎.Young Chen

    算法:详解布隆过滤器的原理.使用场景和注意事项@知乎.Young Chen 什么是布隆过滤器 本质上布隆过滤器是一种数据结构,比较巧妙的概率型数据结构(probabilistic data struc ...

最新文章

  1. vs2012与win7不兼容问题
  2. 部署SAP HANA之前你需要知道的那些事
  3. standford lessons
  4. CSDN博客图片水印|自定义水印|去除水印
  5. django model中的DateField()转为时间戳
  6. 转 8天入门wpf—— 第六天 细说控件
  7. Flex 4中组件背景设置(填充方式)group为例子
  8. python是什么-Python 是什么
  9. 法学类计算机专业,就业蓝皮书:计算机类专业领跑薪酬榜 法学专业被亮“红牌”...
  10. 团队建立伊始的混沌状态(Thinking in CTO)
  11. 网易云音乐推荐系统特训_笔记
  12. 客户端性能测试工具-Wetest、cude PC
  13. 肖博高考数学二轮复习方法之立体几何解题策略-付题型解析
  14. LVM -逻辑卷管理
  15. Excel 轻松制作 二级联动 下拉列表清单
  16. android手机邮件6,Android手机邮箱设置详细教程
  17. 原装安装版WIN10安装详细教程
  18. 用华为ENSP做一个关于防火墙的小实验-华为eNSP怎样调出右侧接口列表
  19. 数据库,计算机网络、操作系统刷题笔记20
  20. css及css3学习

热门文章

  1. 软件侵入式设计和非侵入式的区别
  2. Python在振动信号处理中的应用(十):三分之一倍频程谱的计算
  3. 如何获取红米手机5A的Root权限
  4. 人工智能领域的顶级学术期刊大全(一)
  5. 走访江南大学物联网工程学院
  6. 【DETR源码解析】三、Transformer模块
  7. 51单片机独立按键控制LED状态
  8. 分解连续自然数的和_[算法]正整数分解为几个连续自然数之和
  9. 基于人脸关键点的疲劳检测
  10. 【计算机图形学 】Cohen-Sutherland 直线裁剪算法 | OpenGL+鼠标交互