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. python 猜数字游戏(多版本)

    原始版本 print('------------------你是sb------------------') temp = input("不妨猜一下小甲鱼现在心里想的是哪个数字:" ...

  2. python get 请求接口 忽略证书验证

    import requests # 请求接口 import ssl context = ssl.create_default_context() context.check_hostname = Fa ...

  3. 一场 Kafka CRC 异常引发的血案

    一.问题概述 客户的生产环境突然在近期间歇式的收到了Kafka CRC的相关异常,异常内容如下 Record batch for partition skywalking-traces-0 at of ...

  4. 通用能力及AI核心能力表现优异!合合信息智能文档处理系统(IDP)高评级通过中国信通院评估

    数字经济快速发展的背后,全球数据总量呈现出爆发式增长趋势.智能文档处理(IDP)技术能够高效地从多格式文档中捕捉.提取和处理数据,帮助机构和企业大幅提升文档处理效率,节约时间和人力成本.近期,合合信息 ...

  5. Angular Material 18+ 高级教程 – Material Icon

    前言 不熟悉 Icon 的可以先看这篇 CSS – Icon. Material Design 有自己的一套 Icon 设计.Angular Material 默认就使用这一套. 不过呢,目前 v17 ...

  6. Angular 18+ 高级教程 – Component 组件 の Query Elements

    前言 Angular 是 MVVM 框架. MVVM 的宗旨是 "不要直接操作 DOM". 在 Component 组件 の Template Binding Syntax 文章中 ...

  7. JavaScript – 数据类型

    前言 写着 TypeScript 学习笔记, 顺便也写点 JS 的呗. 参考 JS数据类型分类和判断 阮一峰 – 数据类型 JS 数据类型 string number boolan undefined ...

  8. C++ 模板(函数模板与类模板)

    模板 模板介绍 C++提供了函数模板(function template).所谓函数模板.实际上是建立一个通用函数,其函数类型和形参类型不具体制定,用一个虚拟的类型来代表.这个通用函数就称为函数模板. ...

  9. form data 与request payload的区别以及php接收这些数据的方法

    form data 与request payload的区别以及php接收这些数据的方法 以前与前端交互一直都是POST.GET的,PHP端就直接$_POST,$_GET来接收,从来没有出现过以外. 最 ...

  10. 使用VNC连接ubuntu16.4错误Authentication Failure问题

    解决办法:是因为vnc用一套自己的密码系统,不要去输入ssh登录时的密码,所以只需要进入远程服务器中,设置一哈vnc的密码即可! 在终端输入命令:vncpasswd 到此可以试试远程