走近Java模块化系统OSGi

原文链接:https://www.tianmaying.com/tutorial/osgi-kickstart

1. OSGI是什么?

      刚入软件开发行业的初哥可能会觉得到处都是值得顶礼膜拜的大神,到处都是复杂到自已无法把握的代码,惊叹这些大神怎样能写出如此神奇的程序出来?!其实真正好的软件的代码,应该是结构清晰,简单易懂的代码(别提linux内核代码,那是另类)。说到底,软件设计就不外乎复用、内聚、藕合三个主题。OSGI作为Java的模块化规范,也是为了更好地解决java在这三个主题的问题。

      要理解OSGI,首先要知道OSGI不是一个应用层面的框架,而是设计层面的规范,所以不要用理解spring、hibernate、structs那样的框架的方式来理解OSGI,如果一定要找一个类似的东西和OSGI对比的话,我想会是OO(Object-Orient面向对象)。所以,不要问“怎么将spring和OSGI集成?”这样的问题,就像你不会问“怎么将spring和OO集成?”一样。

       其次,OSGI的目的是模块化,就是为了将一个大的应用分解成较小的模块,这些模块物理上就是一个个的jar包,也就是OSGI bundle。OSGI规范就是指导怎么令这些bundle能更好的有高内聚性、有松藕性,能更好地被复用。至于被神化的“动态性”、“热插拔”的特性,则是OSGI规范带来的一种可能,并不是一定会有的。这些特性是需要配合好的设计才能达到。

       最后,OSGI的课程不是去教你学会一种开发语言,也不是去教你学用一套应用框架,而是教你去设计你的应用,是一种形而上的课程,需要个人去领悟。

2. OSGI framework和bundle的生命周期

       OSGI规范定义了一个叫OSGI framework的平台,这个平台是一个运行在JVM上的应用。它负责管理我们上节提到的bundle(也就是一个符合OSGI规范的Jar包)。

       对于bundle,我们需要关注它的生命周期。

走近Java模块化系统OSGi

      <1>首先,bundle需要install到osgi framework上,这个install只是让framework用一个classloader来装载bundle里的类和资源。

      <2>接着就是resolve,检查bundle需要“引用”的package是否可用,这个后续再详细介绍。

      <3>然后,bundle就要start,也就是运行activator里的start方法,方法运行完毕,即进入"ACTIVE"的状态,这时bundle就算正式可用了。

3. bundle的隔离

        “模块化”的模块是一些相对独立的实体,它们是有边界的,就好象我们在OO里看到,类也是有边界的,它一般是以一个类文件作为边界,而OSGI模块的边界则是一个Jar包,也就是一个bundle。简单的说:在OSGI范畴内,模块就是一个bundle,一个bundle就是一个模块。

        在传统的java应用开发中,我们很少去关注jar包的隔离性,通常只需要知道我们的应用都用到哪些jar包,在部署时,将这些jar包一起部署,就可以用jar包里的java类了。也就是说,在运行时,一堆的jar包并不需要我们去特意去分辨它们之间的关系,部署好后,该用到谁就自然能用上谁,整个应用就是一体的,jar包的依赖关系并不清晰。

        而在OSGI规范下,bundle是被“有计划地”依赖着,你得显式地说明模块之间的依赖关系。OSGI是利用JVM的classloader和它的父委托模型(PDM:Parent Delegation Mode)来实现这点的。

        故名思义,classloader就是加载java类的加载器,一个classloader加载的类,就只能被这个classloader及其子classloader加载的其它类访问到。也就是说,如果两个不是父子关系的classloader加载的类是互相不可见的。

        而OSGI对每一个bundle都分别用一个classloader来加载里面的类,所以不同bundle之间的类,在默认情况下,是互不可见的。

        如果没有额外处理,一个bundle里的类要访问另一个bundle里的类时,通常会出现ClassNotFound的异常,可以说这个异常将是OSGI初学者最常见到的异常。

4. bundle之间的藕合

        一般来说,单个的bundle并不是完整的应用,它需要和其它bundle组合在一起才能真正发挥作用。在上面,我们提到由于每个bundle的classloader都不同,所以它们的类是互不可见的。为了能引用别的bundle的类,osgi通过import/export package的机制来控制bundle间有限地藕合。

        在bundle的设计时,我们将需要给其它bundle“访问”的类放在若干个package内,然后export这些package,就可以让其它bundle通过import package的方式“访问”到这些package里的类,而没被Export的package里的类则被“保护”起来。

        注意:Export/Import package是通过bundle里的META-INF/manifest.mf文件里指定的。

5. 更松散的藕合-osgi service

        除了通过import/export package的机制实现bundle间的藕合,我们还可以通过osgi service的方式实现藕合。osgi service是osgi规范中定义的一种本地服务的机制,“本地”意味着它只是在osgi framework内有效,不可跨osgi framework调用,更不可跨JVM调用。osgi service可以认为是一种“微服务”,可以在bundle之间引用。

        osgi framework有一个service registry,bundle可以把一个实现某种接口的bean实例作为osgi service注册(register)到service registry上,其它bundle就可以从service registry上发现并引用它,所以,本质上osgi service就是一个bean。

         通常,我们会把接口定义在一个bundle A里,接口的实现则在另一个bundle B里,并将接口实现实例化后注册成osgi service,而第三个bundle C则引用这个osgi service。

         因为bundle B和C都需要用到bundle A的接口定义,所以bundle A需export接口定义所在的package,而bundle B和C则需import这个package。这样bundle B和C之间就不需用export/import package来藕合了,实现B和C之间的解藕。在osgi的应用中,会有大量的osgi service存在,可以说osgi service是osgi规范中最重要的机制,没有之一。

6. 其它机制

         OSGI规范还提供了Event、配置管理(ConfigAdmin)、声明式服务(Delarative Service)、Service Tracker、Blueprint等等运行时机制,方便我们构建模块化的应用系统,这些机制,以后再介绍。