1、深入解析Apache Mina源码(1)——Mina的过滤器机制实现

2、深入解析Apache Mina源码(2)——Mina的事件模型

3、深入解析Apache Mina源码(3)——Mina的线程池模型

4、深入解析Apache Mina源码(4)——Mina编解码以及对粘包和断包的处理

一、责任链模式的本来面目

Mina 中有一个重要的设计模式-责任链模式,它将此模式成功的应用在了它的过滤器链(IoFilterChain)中。开发过J2EE的童鞋们应该对这个模式比较清楚,在Servlet中我们就可以加入Filter的实现,还有Hibernate中也实现了FilterChain。学习怎么实现责任链模式对于我们对开源软件的学习和以后的工作都会有莫大的帮助。

我们知道计算机系统就是接收信息,进行信息处理,信息输出的过程,那么在这个过程中我们加入过滤器有很多好处,消息经过特定功能的多个过滤器进行逐步处理得到所需要的最终信息,让处理过程透明,责任明晰,方便扩展。

责任链模式的定义我也就不说了,网上一大堆,我的理解是可以把它理解成一个有向链表,消息在这个链表中的某个过滤器进行处理然后再传向下个过滤器,在每个过滤器中都可以处理这些消息,也可以不处理。如图:

责任链模式的类图如下:

其实这里面最核心的就是Handler了,它可是是一个接口或者是一个抽象类,它有对自己的一个引用successor,然后有个处理方法HandleRequest()。因为有了对自己的引用successor所以当本对象不能处理的时候可以转向successor.HandleRequest()方法,这样一个“链”就形成了。

你应该挺羡慕富士.康里面生产苹果手机的工人吧,你平时最喜欢的苹果手机就是他们生产出来的,他们一个人就能把苹果手机生产出来吗?不是的,他们是流水线做业的,OK,流水线就可以看做是责任链模式的体现,工人就是链条上的一员,有的工人组装屏幕,有的工人检查手机,有的工人进行手机的包装等,下面我们就以代码的方式实现:

苹果手机类:

Java代码  
  1. package com.lifanghu.chain;
  2. /**
  3. * @author lifh
  4. * @mail wslfh2005@163.com
  5. * @since 2012-6-12 下午11:20:18
  6. * @name com.lifanghu.chain.Iphone.java
  7. * @version 1.0
  8. */
  9. public class Iphone {
  10. private String state = "我是一台苹果机器;";
  11. public String getState() {
  12. return state;
  13. }
  14. public void setState(String state) {
  15. this.state = state;
  16. }
  17. }

抽象处理接口类:

Java代码  
  1. package com.lifanghu.chain;
  2. /**
  3. * @author  lifh
  4. * @mail    wslfh2005@163.com
  5. * @since   2012-6-12 下午11:23:36
  6. * @name    com.lifanghu.chain.IWorker.java
  7. * @version 1.0
  8. */
  9. public interface IWorker {
  10. /**
  11. * 处理方法
  12. * @param iphone
  13. * @author lifh
  14. */
  15. void handleIphone(Iphone iphone);
  16. /**
  17. * 设置下一个处理者
  18. * @param worker
  19. * @author lifh
  20. */
  21. void setNext(IWorker worker);
  22. }

具体处理者:

Java代码  
  1. package com.lifanghu.chain;
  2. /**
  3. * @author lifh
  4. * @mail wslfh2005@163.com
  5. * @since 2012-6-12 下午11:29:46
  6. * @name com.lifanghu.chain.Worker1.java
  7. * @version 1.0
  8. */
  9. public class Worker1 implements IWorker {
  10. private IWorker next;
  11. public void handleIphone(Iphone iphone) {
  12. iphone.setState(iphone.getState() + "我被装了一个黑色的后盖;");
  13. if (next != null)
  14. next.handleIphone(iphone);
  15. }
  16. public void setNext(IWorker worker) {
  17. this.next = worker;
  18. }
  19. }
  20. package com.lifanghu.chain;
  21. /**
  22. * @author lifh
  23. * @mail wslfh2005@163.com
  24. * @since 2012-6-12 下午11:34:32
  25. * @name com.lifanghu.chain.Worker2.java
  26. * @version 1.0
  27. */
  28. public class Worker2 implements IWorker {
  29. private IWorker next;
  30. public void handleIphone(Iphone iphone) {
  31. iphone.setState(iphone.getState() + "我被装了一块电池;");
  32. if (next != null)
  33. next.handleIphone(iphone);
  34. }
  35. public void setNext(IWorker worker) {
  36. this.next = worker;
  37. }
  38. }
  39. package com.lifanghu.chain;
  40. /**
  41. * @author lifh
  42. * @mail wslfh2005@163.com
  43. * @since 2012-6-12 下午11:34:32
  44. * @name com.lifanghu.chain.Worker3.java
  45. * @version 1.0
  46. */
  47. public class Worker3 implements IWorker {
  48. private IWorker next;
  49. public void handleIphone(Iphone iphone) {
  50. iphone.setState(iphone.getState() + "我现在是一台完整的Iphone了。");
  51. if (next != null)
  52. next.handleIphone(iphone);
  53. }
  54. public void setNext(IWorker worker) {
  55. this.next = worker;
  56. }
  57. }

客户端调用者:

Java代码  
  1. package com.lifanghu.chain;
  2. /**
  3. * @author lifh
  4. * @mail wslfh2005@163.com
  5. * @since 2012-6-12 下午11:37:27
  6. * @name com.lifanghu.chain.Client.java
  7. * @version 1.0
  8. */
  9. public class Client {
  10. public static void main(String[] args) {
  11. IWorker worker1 = new Worker1();
  12. IWorker worker2 = new Worker2();
  13. IWorker worker3 = new Worker3();
  14. worker1.setNext(worker2);
  15. worker2.setNext(worker3);
  16. Iphone iphone = new Iphone();
  17. worker1.handleIphone(iphone);
  18. System.out.println(iphone.getState());
  19. }
  20. }

输出结果:

 写道
我是一台苹果机器;我被装了一个黑色的后盖;我被装了一块电池;我现在是一台完整的Iphone了。

好了,现在一台苹果就组装完成了,发现了没有,我们的代码比较整洁清新,没有大量的if/else语句,责任清晰明了,调用简单,想要扩展的话只需要再实现IWorker接口即可,这就是使用责任链模式的好处。

二、Mina是怎么实现责任链模式的

上面介绍了纯净的责任链模式,但是在真实的项目中写代码不可能完全照搬,所以多看看开源项目的代码写作方式也许才能真正提高我们的编程能力。就以Mina的过滤器链来看,对这种模式进行了自己的实现,但是道理是相通的,带着责任链模式的理解去看看Mina的实现是怎样的。

先看一下Mina过滤器的类图结构:

从图中我们可以看出Mina对于此模式的实现是相对比较复杂的,其实从它的代码上也可以看出来,当时也是花了好大力气才把它这块看明白,其实主要在于理清思路,抓住核心类,最主要的一个接口IoFilterChain和它的一个内部接口Entry,其中IoFilterChain代表了过滤器的容器,它本身就是一个对象引用形成的链表结构,默认的实现DefaultIoFilterChain其实有对链表头(head)的引用,找到头后就可以顺着头向下找,一直找到尾(tail),Entry是对IoFilter和NextFilter的封装整合接口,它属于链表IoFilterChain中的元素。指向当前和下一个过滤器。

EntryImpl 类中包含两个接口,filter和nextFilter,他们所用的接口是不一样的,至于为什么不用同一个接口,我想可能是因为接口职责单一的原则吧。

虽然有IoFilter和NextFilter两个接口,接口方法都差不多,但最后真正业务的执行者还是IoFilter的实现,IoFilterAdapter做为它的默认实现,完成了适配器的功能,以后的类可以直接继承它而不用实现IoFilter接口,想实现哪个方法只需要覆盖IoFilterAdapter的类的方法即可。NextFilter只起到转发的作用,如消息接收处理方法(messageReceived):

NextFilter的实现:

Java代码  
  1. //消息接收到以后NextFilter的实现会把消息传递给nextEntry去处理。
  2. public void messageReceived(IoSession session, Object message) {
  3. Entry nextEntry = EntryImpl.this.nextEntry;
  4. callNextMessageReceived(nextEntry, session, message);
  5. }

DefaultIoFilterChain再将消息传递给本Filter的messageReceived方法进行实际的消息接收处理工作:

Java代码  
  1. private void callNextMessageReceived(Entry entry, IoSession session,
  2. Object message) {
  3. try {
  4. IoFilter filter = entry.getFilter();
  5. NextFilter nextFilter = entry.getNextFilter();
  6. //本过滤器进行数据处理,完成后再把消息传向下个过滤器
  7. filter.messageReceived(nextFilter, session,
  8. message);
  9. } catch (Throwable e) {
  10. fireExceptionCaught(e);
  11. }
  12. }

当消息处理完成以后,消息就要进入到业务处理IoHandler的实现类中去完成业务的相关操作了。这可以在链尾看到相关业务传递:

Java代码  
  1. @Override
  2. public void messageReceived(NextFilter nextFilter, IoSession session, Object message) throws Exception {
  3. AbstractIoSession s = (AbstractIoSession) session;
  4. if (!(message instanceof IoBuffer)) {
  5. s.increaseReadMessages(System.currentTimeMillis());
  6. } else if (!((IoBuffer) message).hasRemaining()) {
  7. s.increaseReadMessages(System.currentTimeMillis());
  8. }
  9. try {
  10. // 最后一个filter会进入到handler进行消息处理。
  11. session.getHandler().messageReceived(s, message);
  12. } finally {
  13. if (s.getConfig().isUseReadOperation()) {
  14. s.offerReadFuture(message);
  15. }
  16. }
  17. }

至此,Mina的链表结构就基本讲完了,其实仔细看看它的代码还是比较容易理解的,可能里面有些细节还需要我们去琢磨。

链表建立了以后,外界是怎样调用的呢?

在org.apache.mina.core.filterchain包下我们可以看到类IoFilterEvent,可以看到它实现了基于事件的处理模型,当一个事件(比如接收到消息)发生后会触发相应的事件,进而调用过滤器链的消息处理功能进行消息的处理和转发,这块其实会有线程池的参与完成,会在以后的文章中说明这块。

Java代码  
  1. public void fire() {
  2. IoSession session = getSession();
  3. NextFilter nextFilter = getNextFilter();
  4. IoEventType type = getType();
  5. if (DEBUG) {
  6. LOGGER.debug( "Firing a {} event for session {}",type, session.getId() );
  7. }
  8. switch (type) {
  9. case MESSAGE_RECEIVED:
  10. //触发消息接收
  11. Object parameter = getParameter();
  12. nextFilter.messageReceived(session, parameter);
  13. break;

当然也可以直接调用:

Java代码  
  1. //触发过滤器的接收消息处理
  2. session.getFilterChain().fireMessageReceived(newBuf);

还有一个类DefaultIoFilterChainBuilder,它以用户更易用的角度完成了对过滤器链的封装操作,这里就不细讲了。

总结:本文章主要讲了责任链模式在Mina中的实现,做为一个优秀的框架我们没理由不好好去学习。

每天进步一点点,不做无为的码农。。。。。

2012年6月13日星期三

码农虎虎

http://weibo.com/hurtigf

http://www.lifanghu.com/

wslfh2005@163.com

深入解析Apache Mina源码(1)——Mina的过滤器机制实现的更多相关文章

  1. NIO框架之MINA源码解析(五):NIO超级陷阱和使用同步IO与MINA通信

    1.NIO超级陷阱 之所以说NIO超级陷阱,就是因为我在本系列开头的那句话,因为使用缺陷导致客户业务系统瘫痪.当然,我对这个问题进行了很深的追踪,包括对MINA源码的深入了解,但其实之所以会出现这个问 ...

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

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

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

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

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

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

  5. apache flink源码挖坑 (未完待续)

    Apache Flink 源码解读(一) ​ By yyz940922原创 项目模块 (除去.git, .github, .idea, docs等): flink-annotations: flink ...

  6. 详细解析Thinkphp5.1源码执行入口文件index.php运行过程

    详细解析Thinkphp5.1源码执行入口文件index.php运行过程 运行了public目录下的index.php文件后,tp的运行整个运行过程的解析 入口文件index.php代码如下: < ...

  7. Apache Spark源码剖析

    Apache Spark源码剖析(全面系统介绍Spark源码,提供分析源码的实用技巧和合理的阅读顺序,充分了解Spark的设计思想和运行机理) 许鹏 著   ISBN 978-7-121-25420- ...

  8. 《Apache Spark源码剖析》

    Spark Contributor,Databricks工程师连城,华为大数据平台开发部部长陈亮,网易杭州研究院副院长汪源,TalkingData首席数据科学家张夏天联袂力荐1.本书全面.系统地介绍了 ...

  9. Apache Kafka源码分析 – Broker Server

    1. Kafka.scala 在Kafka的main入口中startup KafkaServerStartable, 而KafkaServerStartable这是对KafkaServer的封装 1: ...

  10. rest_framework解析器组件源码流程

    rest_framework解析器组件源码流程 解析器顾名思义就是对请求体进行解析.为什么要有解析器?原因很简单,当后台和前端进行交互的时候数据类型不一定都是表单数据或者json,当然也有其他类型的数 ...

随机推荐

  1. LCD屏幕显示PNG图像

    正点原子LCD屏幕显示PNG图像 本文概要 这段时间在学习正点原子的IMX6ULL开发板,在应用编程中有一个代码练习是需要在LCD屏幕上显示PNG图像,但由于我的屏幕参数和教程中的有些出入,于是经过自 ...

  2. java基础 -线程(基础)的 笔记

    581,多线程机制 因为需要敌人的坦克可以自由移动并发射子弹,我们的坦克可以移动并发射子弹,这些要用到线程的知识. 根据JConsole监控线程执行情况,发现,主线程执行完了,子线程还没有执行完,并不 ...

  3. WebRTC 简单入门与实践

    一.前言 WebRTC 技术已经广泛在各个行业及场景中被应用,但对多数开发者来说,实时音视频及相关技术却是比较不常接触到的. 做为一名 Web 开发者,WebRTC 这块的概念着实花了不少时间才搞明白 ...

  4. 小tips:前端容易读错的单词列表

    排名第一的是width,音标/wɪdθ/,发/i/的音,不是发/ai/的音: hidden音标/ˈhɪdn/发/i/的音,不是发/ai/的音: hide音标/haɪd/,发/ai/的音: float音 ...

  5. 搭建ipv6并发代理池

    声明 本文章中所有内容仅供学习交流,抓包内容.敏感网址.数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除! 学习目标 ounter(l ...

  6. SpringMVC——SSM整合-异常处理器

    异常处理器 出现异常的常见位置与常见诱因: 框架内部抛出的异常:因使用不合规导致 数据层抛出异常:因外部服务器故障导致(例如:服务器访问超时) 业务层抛出的异常:因业务逻辑书写错误导致(例如:遍历业务 ...

  7. Wpf使用NLog将日志输出到LogViewer

    1 LogViewer LogViewer是通过UDP传输的高性能实时log查看器. 具有一下特性: 通过UDP读取日志 通过文件导入日志 导出日志到一个文件中 排序.过滤(日志树,日志等级)和查找 ...

  8. 0608-nn和autograd的区别

    0608-nn和autograd的区别 目录 一.nn 和 autograd 的区别 二.Function 和 Module 在实际中使用的情况 pytorch完整教程目录:https://www.c ...

  9. day15-三大基本结构

    顺序结构 Java的基本结构就是顺序结构,除非特别指明,否则就按照顺序一句一句执行. 顺序结构是最简单的算法结构. 语句和语句之间,框与框之间是按从上到下的顺序进行的,它是由若干个依次执行的处理步骤组 ...

  10. G-数据结构-G

    \[\huge 近日多做数据结构题,或恐后再读不能醒悟,或记其思路,或骂出题人,或不想刷题,虽有此篇. \] \[\] \[\] \[\] \[\] T1 距离 首先这题部分分很多,直接 $ O (n ...