OSGI之模块层

阅读更多

前言

 

OSGI的精髓就是通过一个个的模块构建起整个应用系统,相对于普通开发模式而已,OSGI会强制开发人员对系统进行模块划分。因此如何对系统进行模块化的划分就显得很重要了,基本原则就是高内聚低耦合(在bundle内部高内聚,在bundle之间低耦合),每个团队负责指定的模块开发。指定好每个bundle的边界和职责,然后分别进行开发实现,这种方式非常有利于并行开发,提高工作效率。

 

传统的MVC开发模式一般会把系统分为Controller层、Service层、Dao层等等,每层一般会打成一个jar包。而OSGI的模块化概念一般建议以具体的业务功能进行模块划分,比如:登陆模块、a业务模块、b业务模块、数据操作模块(类似于Dao层)等待,各个模块之间有依赖关系,并且这些模块是可以独立更新的(热更新)。

 

OSGI的模块层规范,主要就是对如何进行模块化指定了相关规范,简单的理解就是如果配置模块的元数据文件MANIFEST.MF

 

MANIFEST.MF配置详解

 

元数据文件MANIFEST.MF的配置信息分为三类:描述信息配置、唯一识别配置、可见性配置。描述信息配置:主要作用就是让使用者了解这个bundleOGGI框架不会直接使用这些配置信息;唯一识别配置:这类配置信息主要作用就是用于在OSGI框架中唯一确定一个bundle;可见性配置:OSGI的模块化分为物理模块化和逻辑模块化,物理模块化的理解很简单就是把那些类放到bundle对应的jar包中,逻辑模块化就是bundle对内对外的可见边界,可见性配置的主要作用就是如何实现bundle的逻辑模块化。

 

描述信息配置

这部分是可选的,主要提供一些描述信息,方便使用者查阅。OGGI框架不会直接使用这些配置信息,也没有太多注意事项,根据业务需要选择性的使用即可:

Bundle-Name: my osgi test  //根据自己的喜好随便写

Bundle-Description: do some test  //描述信息,该模块是做什么的

Bundle-DocURL:http://www.xxxx.com  //帮助文档地址

Bundle-Category: test //Bundle分类,随便写

Bundle-Vendersky  //Bundle供应商,是谁开发的

Bundle-ContactAddress: //供应商 联系地址

Bundle-Copyright: xxxx //供应商版权信息

 

唯一识别配置

是识别bundle的必须配置的信息。一共三个配置项:

Bundle-SymbolicName: com.sky.osgi.test

Bundle-Version: 1.0.0

Bundle-ManifestVersion: 2

Bundle-SymbolicName

Bundle-SymbolicNameBundle唯一标识符,一般与java包的命名规则类似(域名反转),也可以选择其它命名方式,主要保证名字的唯一性即可。

Bundle-Version定义了Bundle的版本号,理论上Bundle-SymbolicNameBundle-Version一起才可以保证在同一个OSGI运行框架下的唯一性。配置值 一般采用x.x.x的方式,其中x是一个数字,数字越大说明版本越新,另外也可以是采用1.1.1.beta表示版本号。

Bundle-ManifestVersionOSGI规范的版本有关,不用过多解读,只需要记住是强制配置,并且值为2即可。

 

可见性配置

实现对bundle模块化的边界控制,是OSGI实现模块化的核心配置。主要用于描述类是Bundle内部可见,哪些类需要导出提供给其他Bundle使用。元数据的可见性配置包含下列三项:

Bundle-ClassPath:.,xxxx.jar

Import-Packagecom.sky.osgi.client; version=”[1.0.0,2.00)”

Export-Packagecom.sky.osgi.server; version=”1.0.0”

 

内部类路径

Bundle-ClassPath定义了Bundle的类路径,在OSGI框架里运行的每个Bundle都会使用一个独立的类加载器进行类加载,java是按需加载的 也就是在执行过程中用到了哪个类,就会到ClassPath下去找相应的class文件加载到方法区。Bundle-ClassPath配置的路径就是该Bundle对应的ClassLoader加载class文件的路径。

 

Bundle-ClassPath的默认路径是”.”,表示只加载当前jar包内的class文件。同时可以配置多个路径 并用号间隔,这些路径都是相对于当即jar包位置的相对路径,ClassLoader查找顺序就是按照这个顺序进行。注意内嵌的jar包,是不能被直接加载的,需要配置到Bundle-ClassPath中才能被ClassLoader查找到。

 

导出内部代码

Export-Package配置项的作用是到出内部代码给其他Bundle使用,如果不配置该项 表示该Bundle不公开任何代码,只公开Export-Package配置值中配置的包路径下的类(不包含包下的类)。比如:Export-Packagecom.sky.osgi.server; version=”1.0.0”,这个配置表明这个Bundle值公开了com.sky.osgi.server包下的类,其它即便是定义为Public的类 都是不公开的。

 

Export-Package可以导出多个包,其它Bundle可以通过Import-Package来导入自己需要的包。

 

导入外部代码

Import-Package配置项的作用是根据自己的需要导入其他Bundle公开的包,注意与Bundle-ClassPath的区别:Import-Package指定的包,不是自己的类加载器加载的,而是导出这个包的BundleClassLoader加载的;而Bundle-ClassPath指定的路径是该Bundle自己的ClassLoader指定的路径。

 

同样的Import-Package也可以导入多个包。

 

另外  DynamicImport-Package: *,可以一次动态的导入多个需要的包,但由于存在相互依赖等问题不建议使用。

 

关于包的版本

 

Export-PackageImport-Package一个包时,指定version版本号。在Export-Package导出时,可以指定一个特定的版本号;在Import-Package导入时可以指定一个特定的版本,也可以指定一个版本的区间,“[]”表示闭区间,“()”表示开区间,同时也可以是半开半闭。

比如:

Import-Packagecom.sky.osgi.client; version=”1.0.0” //表示指定版本为大于等于1.0.0

Import-Packagecom.sky.osgi.client; version=”[1.0.0,2.0.0)” //表示大于等于1.0.0,小于2.0.0

 

如果指定一个特定的版本,适配的区间为大于等于这个版本,如果不指定版本默认是0.0.0,也就是大于等于0.0.0,本质上也是一个区间。另外注意区分 包的版本与Bundle版本,前面讲的Bundle-Version是指定Bundle的版本。

 

Bundle内部加载顺序

 

Bundle执行需要一个类时,会按照下列顺序进行查找:

1、如果类的包名是以java.开头的(也就是jdkrt.jar中的类),会交给当前类加载器的父类加载器去查找并加载这个类。这种方法的好处是,对于JDK的公共API不需要多次加载。如果没有找到就抛出异常。

2、如果这个类的包路径在Import-Package配置的路径下,OSGI框架会委托给导出这个包的Bundle对应的类加载器查找并加载该类。如果没有找到直接抛出异常。

3、如果这个类的包路径在Bundle-ClassPath配置的路径下,会直接使用该Bundle自己的类加载器查找并加载该类。如果没有找到就抛出异常。

通过这3步查找顺序可以确定一个类在Bundle内部的一致性。

 

Bundle之间的一致性

 

目的:OSGI框架必须保证在多个Bundle中使用同一个版本的导出包。

由于Import-Package导入可能会遇到多个版本,那当前Bundle该如何选择呢?自动安装Bundle时 需要满足的原则是:

1、已解析的Bundle具有最高优先级,如果匹配到多个已解析的Bundle,按照先版本号后安装顺序进行匹配。

2、如果都未解析,同样是先版本号后安装顺序进行匹配。

所谓解析就是把给定Bundle的导入包与其它Bundle的导出包进行匹配的过程。解析依赖具有传递性:BundleA依赖BundleBBundleA依赖BundleC,在解析BundleA时之前,必须先依次对BundleCBundleB进行解析。

 

上述原则OSGI框架加载时自动遵守的。但也有特例,在动态加入Bundle时(在首次启动OSGI框架之后加入Bundle),有可能出现由于传递依赖导致的冲突,为了解决这个冲突在Export导出时,需要使用uses指令。例如:Export-Package: com.sky.server;uses:="com.sky.client"; version="2.0.0"  。这种场景不是很多,在具体用到时可以再查阅。

 

 

本文内容部分参考自《OSGI实战》

你可能感兴趣的:(OSGI模块层)