最近一直在看Mina的源码,用了Mina这么长时间,说实话,现在才开始对Mina有了一些深刻的理解,关于Mina的基本知识的介绍,这里就不多说了,网上已经有很多不错的文章都对Mina做了较深刻的剖析,现在就是想从Mina的最根本的地方来对Mina做一些深层次上的探讨。

 

还是先从Mina的入口程序来说,每当要启动一个Mina的程序(包括服务器和客户端)时候,这里只是对服务器重点做一些讲解,至于说Mina的客户端的应用,这里只是简单的涉及一点,不会对其做很深入的探讨。但是Mina的服务器和客户端在很大的程度上都是一样,所以这里就“挂一漏万”的简单讲解一下。

 

在此之前我一直想找一种“串糖葫芦”的方式来讲解一下Mina,可是一直没有时间来看Mina的源码,真的是无从下手,虽然网上的很多关于Mina的一些文章,讲解的非常透彻了,但是可能对于初学者来说,显得有些深奥,在这里特别的提一下洞庭散人对Mina源码的透彻的分析,如果你对Mina已经有了一定的了解或者是正在学习Mina的源码,建议你去看看他的博客,里面有很多东西讲的是相当到位的。在这里就不在多举例子了。写这篇文档主要是想对刚接触Mina的人讲解一些Mina的基本知识,由浅入深,一步一步的学习Mina思想的精髓,我接触Mina的时间也比较长了,几乎天天在和它打交道,每当你发现一个新奇的用法的时候,你真的会被Mina所折服,我这里不是对Mina的吹捧,记得我曾经和同事开玩笑说,“等真正的懂得了Mina,你就知道什么叫Java了”,所以,我现在想急切的把现在所知道和了解的所有关于Mina的一些东西都想在这篇文章里面写出来,如果有写的不到位的地方还请各位同学多多指正,下面就开始对Mina做一个完整的介

绍。

 

先说说Mina的几个类和接口
(1) IoService
(2) BaseIoService
(3) BaseIoAcceptor
(4) IoAcceptor
(5) IoConnector

这几个类和接口是整个服务器或客户端程序(IoConnector)的入口程序,其中就Mina的整体上来说,IoService是所有IO通信的入口程序,下面的几个接口和类都是继承或者实现了IoService接口。

 


下面先给出Mina(入口程序)的整体架构图:

                                                                Mina的整体架构图

 

在这里先提出几个个问题:
(1)为什么有了一个IoService还要再有一个BaseIoService?
(2)BaseIoService和IoAcceptor(IoConnector)有什么区别?
(3)BaseIoAcceptor(BaseIoConnector)为什么不去直接实现IoService,而是又添加了

        IoAcceptor(IoConnector)?
   
带着这几个问题我们来解读一下Mina的源码:
首先,解答第一个问题,为什么有了一个IoService还要再有一个BaseIoService?IoService和BaseIoService最明显的区别就是IoService是一个接口,而BaseIoService是一个抽象类。BaseIoService实现了IoService中的部分方法。

 

这里先把IoService接口中要实现的方法和BaseIoService中实现的方法列举如下:

 

 



 

通过对IoService和BaseIoService的比较可以发现,除了getDefaultConfig()这个方法没有在BaseIoService中实现之外,其他的方法都已经在BaseIoService实现了。这里就有一个问题,为什么BaseIoService只是实现了IoService的部分方法,而没有全部实现IoService的方法呢?通常都知道,接口中的方法是必须要由实现类来实现的,这点是毋庸置疑的。你可以写一个空方法,里面没有任何的逻辑处理,但是你的实现类中却不能没有该方法。但是在Mina中作为实现类的BaseIoService却没有IoService指定的方法getDefaultConfig(),难道Mina真的有独到之处?不是!仔细看看

BaseIoService你就会知道,BaseIoService是一个抽象类,抽象类就是用来被继承的,它提供了一些其子类公用的一些方法,当抽象类实现一个接口时,抽象类可以有选择性的实现其所有子类都需要的实现的一些方法,对于接口中指定法方法,抽象类可以选择全部实现或者部分实现。在Mina中如果没有BaseIoService这个抽象类,而是由BaseIoAcceptor和BaseIoConnector直接去实现BaseIoService接口,那么必然会导致这个两个实现类中都要重写相应的方法,这样就脱离了面向对象设计的本质,没有达到复用的目的。在BaseIoAcceptor/BaseIoConnector和BaseIoService之间添加一个BaseIoService就是为了达到代码复用的目的。在这个问题上主要是要记住两点:
  
   1)抽象类在实现接口的时候可以部分或者全部实现接口中的方法。但是当抽象类只是实 
       现了接口中的部分方法的时候,抽象类的子类必须要实现抽象类中未实现的接口的方
       法。在此处,IoService的getDefaultConfig()方法在BaseIoService(BaseIoAcceptor  
       是BaseIoService的子类,但它也是一个抽象类,所以它也没有实现getDefaultConfig()),

       getDefaultConfig() 是    由BaseIoAcceptor的子类们来实现的(如SocketAcceptor,这是一个

       具体实现类)。所以接口的所有方法必须被具体的实现类实现和抽象类在实现接口的时候可以部分

       或者全部实现接口中的方法是不矛盾的。

 

   2)注意代码的重用。在面向对象的编程语言中都提供了抽象类和接口,抽象类和接口最大的区别

        就是抽象类提供了方法的具体实现,供其子类来调用;而接口只是提供了对方法的声明,其方

        法的实现要由其具体实现类来做。在Java中一个子类只能有一个父类,但是却能实现多个接口。

       个人认为接口和抽象类各有特色,接口的使用比较灵活,不同的接口可以让其子类扮演不同的角

       色,侧重于类的复用,在很大程度上解决了代码复用的问题;抽象类更侧重的是方法的复用,某

       种意义上讲,抽象类的使用对于程序来说使用起来更加轻松,但是是使用抽象类还是接口要根据

       具体的情况而定。


      对于接口和抽象类的具体的用法请参考闫宏的《Java与模式》中相关部分的讲解。

 

之所以在这里罗列这么些问题,目的不仅仅是为了讲解Mina的原理,而是想从一个高的角度来看待的这个经典的开源项目,通过对Mina的学习和理解,能够真正的懂得什么是一个项目,什么是面向对象编程,更本质的东西是怎么灵活运用Java来达到上面的两个目的。这个才是最重要的,哪怕是你在看完本文后对Mina的理解还是有点模糊,但是你至少要知道在编写一个程序的时候怎样从面向对象的角度上去思考一个问题,而不是在用着面向对象的语言写着结构化的程序。这些东西都是自己做开发这么长时间的一些心得,在这里总结出来,目的主要是用于交流和学习,不是在卖弄,只是想让更多的初学者少走一些弯路,懂得学习的方法。

 

还是回到对Mina的刚提出的那几个问题上来,现在,第一个问题已经解决了,为什么有了一个IoService还要再有一个BaseIoService?答案就是为了代码的复用。

 

其次,下面开始讨论第二个问题,BaseIoService和IoAcceptor(IoConnector)有什么区别?
在讨论这个问题之前,还是先给出这两个类(接口)提供的方法,如下图:


      

                                                                                               

在讨论第一个问题的时候我们已经看过了BaseIoService的方法了,但是没有对这些方法的功能做些梳理,现在就对这些方法做些简单的介绍:

 

getFilterChainBuilder()和setFilterChainBuilder():这两个方法主要是对一个服务的IoFilter的操作,关于IoFilter的详细介绍会在后面给出,现在你可以将其理解为是一个处理业务逻辑的模块,例如:黑名单的处理、数据的转换、日志信息的处理等等都可以在这个IoFilter中实现,它的工作原理和Servlet中的过滤器很相似。

 

addListener()和removeListener():这两个方法通过名字看就可以理解了,就是给当前的服务添加和删除一个监听器,这个监听器主要是用于对当前连接到服务的IoSession进行管理,这个也会在后面做详细的讲解。

 

getManagerServiceAddress()和getManagerSessions():这两个方法的功能比较相似,一个是获取当前服务所管理的远程地址,一个是获取当前服务所管理的会话IoSession,IoSession对SocketAddress做了一个完整的封装,你也

可以先将这两个方法的功能理解为是一回事,具体的区别会在后面给出。isManaged():检测某个SocketAddress是否处于被管理的状态。

 

getListeners():获取当前服务的监听器。

 

看了上面对BaseIoService功能的介绍,现在我们可以理解BaseIoService提供的方法主要是用于对当前服务的管理。那么要管理一个服务,前提条件是这个服务必须存在,存在的前提是什么,就是要启动一个服务,或者是连接到一个远程主机上,这两个任务分别是IoAcceptor和IoConnector来完成的,此处要注意的是这两个对象都是接口,没有具体的实现,具体的实现会由下面介绍的它们相关的子类(SocketAcceptor等)来实现。这样IoAcceptor/IoConnector的功能我们就可以总结出来了,就是启动和停止一个服务。

 

对于一个完整的服务来说,既要有启动这样的前提条件,还要有对服务的管理和对服务响应的逻辑处理,这两个缺一不可,回到第二个问题,BaseIoService和IoAcceptor(IoConnector)有什么区别?区别就在于它们实现的功能不一样,但都是为了一个完整的服务来打基础的,两者缺一都不能称为一个完整的服务。这三个都是IoService子类(子接口),oService只是提供了一些服务应该具有多基本的方法,BaseIoService提供了IoService部分方法的具体实现,而IoAcceptor(IoConnector)是对特定服务要具备的操作的做了一些扩展,这样一个服务完整的模型正在逐渐向我们清晰的展现出来。

 

再次,讨论一下第三个问题。BaseIoAcceptor(BaseIoConnector)为什么不去直接实现IoService,而是又添加了IoAcceptor(IoConnector)?这个问题其实在上面已经有所涉及,为了达到对象复用的目的,所以Mina的设计者给出了一个BaseIoService,IoAcceptor(IoConnector)是实现一个特定服务必须要提供的一些方法。更具体一点,IoAcceptor(IoConnector)是为了一个特定的服务(服务器/客户端)而设计的,而IoService只是提供了一个服务应该具备的一些基本的方法。所以在Mina中给出了一个针对具体服务的一个接口IoAcceptor(IoConnector),这样BaseIoAcceptor(BaseIoConnector)就提供了一个服务所必备的一些条件。因为它即实现了IoAcceptor(IoConnector)接口又继承了抽象类BaseIoService,这样就实现了IoService中的所有方法,并且也添加了特定服务应该具有的方法(即IoAcceptor(IoConnector)中的方法)。以上就是第三个问题的答案。

 

 

 

Mina中提供的几个特定的服务
从上面的讨论中我们已经知道了Mina上层的类和接口的一些功能。即图中所示的已经在上面解释清楚了。



 在此我们可以把Mina的上层结构简单的定义为Mina的“抽象层”,既然有了抽象层,肯定就会有其具体实现,抽象中最重要的两个类是BaseIoAcceptor和BaseIoConnector,它们分别是用于服务器和客户端的一个入口程序。

首先,说一下BaseIoAcceptor中的三个具体实现类:


DatagramAcceptorDelegate:数据报UDP通信的服务器入口程序。该类使用UDP协   议进行通信,UDP协议主要是用在视频、远程服务的监听(如心跳程序)中等数据传输   要求不是很高的地方。
VmPipeAcceptor:虚拟通道(VM)通信的服务器入口程序。虚拟管道协议主要用于无线通信方面。             
SocketAcceptor:TCP/IP通信的服务器入口程序。这个是比较常用的协议,该协议主要   数据传输要求较高的地方,比如实时系统、游戏服务器等。


BaseIoAcceptor及其子类
与BaseIoAcceptor相对应的就是BaseIoConnector,该类主要用于客户端程序。其具体的子类就不再赘述,这里只给出BaseIoConnector及其子类的结构图。


BaseIoConnector及其子类

关于SocketAcceptor、IoFilter、IoProcessor、IoHandler等会有专门的文章来讨论。这里就不在对这些组件类做详细的说明了。