OSGI 进阶学习——《OSGI In Practice》 阅读总结

晨曦之光 发布于 2012/03/09 12:11
阅读 555
收藏 1


 

1.   概述

《OSGI In Practice》是一本值得推荐的OSGi方面的著述, 其亮点是对OSGi的原理剖析得相当深刻, 并且能结合实际, 但结合实际上面目前做得不是很好, 这里结合了本人的一些实践经验, 提出了部分更优的实践策略。因此这篇文章希望读者能更多的关注原理性的东西, 读完本篇的收获将是:

1.      OSGi解决了什么问题?

2.      目前通用的方案存在什么问题? 为什么OSGi优于通用方案?

3.      OSGI架构和具体业务之间的是如何通信的?

 

比较遗憾的是, OSGi联盟推广的这本书, 居然在各大书店没有纸质的可购买。但英文版的PDF随处可见。处于分享的目的, 把对本书的总结发出来, 如果有兴趣看英文原版, 可以留下email地址.

2.   基本原理

普通的Java解决方案, 无论是J2Se或者J2EE方案,采用的都是ClassLoader + Jar包的机制,这个机制有以下三个问题(第一个问题是关键)

1.      ClassPath是全局的扁平结构.

a)        首先加载ClassPath中靠前的Jar, 后面实际中要用到的将被忽略

b)        类之间没有体现明确的依赖关系, 重名的类, 导致运行时,使用到其它的类. 从而报错

2.      版本信息问题, 结合问题一, 两个版本的包, 在一个项目被用到, 将混乱不堪.

3.      访问策略问题

a)        Default, public, private,protect 四层访问机制, 封装性依然不够好, 为了访问, 不得不讲所有的类, 都public化, 基本上暴露了全部的类.

b)        Jar包实际上并没有形成一个模块所达到的要求: 高内聚(high-cohesive),松耦合(Loosely Coupled), 自治(Self-contained).

 

很多企业的架构都是采用J2EE的方案, 这个方案存在同样的问题, 下面分析一下。

J2ee的ClassLoader方式:


问题同样存在, 为了解决全局扁平的classpath的问题, 所有应用能共用的包,都不能放在Application ClassLoader中, 只能放在EAR具体应用的Classpaht中。这样造成应用见彼此不能复用的问题。 

 

OSGI 的简单思想:

1.      为Java而来的模块化系统, 做了两件事情

a)        定义了一种创建真正的模块的方式。为什么叫真正的模块, 实际上Jar并非是一个真正的模块, 因为它没有满足上面提到的模块应具备的三大定义(自治, 高内聚,低耦合)。显然jar的public方法太多, 造成模块对外的复杂性很高。这点很蛋疼。

b)        定义了软件系统运行时模块之间交互的方法。

2.      OSGI处理方式的思路(传统Jar带来的问题主要是Java扁平化,全局的Classpath造成, 因此围绕这个问题解决也在此)

a)        每个Bundle/模块都有自己的classpath, 各个模块的classpath独立于其它模块。这样便消除了Jar包的一切问题。当然这个并没有解决Class之间共享的问题。

b)        通过显示的Import和export机制, 实现类的共享。指明模块之间的共享/依赖关系

 

从上面的考虑来看, 为了解决上述问题, 配置的元数据需要包含以下内容(MANIFEST.MF文件中定义内容):

1.      Bundle的名称, 这个需要有唯一性Bundle-SymbolicName.就是插件的名称

2.      插件的版本. 显然这个的引入就是为了摆脱Jar中没有版本带来的烦恼.

3.      Imports和Exports列表.

4.      需要的JDK的最低版本号, 这个是可选的。

5.      其它各种供人而不是机器阅读的信息,Bundle的提供商,版权等.

 

OSGI 的MANIFEST.MF跟Jar的MANIFEST.MF一致, 这样保证能用JAR的地方, 就能用OSGI定义的插件包. 保证了有效的兼容性(OSGI打包的方式跟JAR的方式也是相同的)。

 

OSGI的ClassLoader模式, 不再是标准Java或者是上面J2EE提及到得树形ClassLoader机制, 而是一种图的机制,ClassLoader之间, 不再是Child和Father的关系, 而是Provider和User的关系, 见图:


说明:

1.      箭头的方向表示使用,如A 使用了B提供的org.foo.*包中的类, 又使用了C提供的org.bar.*中提供的类.

2.      B或C通过Exports, 将包提供出来, 通过Import的方式,将制定插件引入.

3.      A使用B中的class, 使用过B的classloader加载进来的. 同理, A使用C的class, 是通过C的ClassLoader加载进来的.

4.      相比于标准Java, OSGI的类加载效率高, 无需搜索。

5.      加载失败, 在初始化的时候, 即产生ClassNotFoundError

 

关于Version:

1.      发布的插件只需要一个版本号, 但import一个需要的插件包可以制定这个包的版本范围,如从1.0.x ~ 2.0.x.

2.      一个Application中, 不同版本的插件可以并存。这个显然超越了Jar包。

 

关于动态模块(Dynamic Modules):

1.      OSGI 不单单是一个java模块化系统, 而且是动态的模块化系统, 各个模块的安装、更新、卸载,无需将整个Application系统停下才能完成。更新插件,无需重启。

2.      实现手段: Life Cycle Layer和Service

3.      几乎所有的开发者, 对Dynamic Modules不够信赖, 用OSGI的初衷, 来自于对模块化需求,很自然的忽视了Life Cycle 这个强大的功能。但注意,Life Cycle Layer绝对不是夸大其词的广告。

3.   OSGI的几个实现

1.      Equinox 一句话: Eclipse工具用得是这个标准

2.      Knopflerfish

3.      Felix 阿帕奇弄出来的, 估计跟Web有点关系.

4.      Concierge 部署在资源有限的终端, 如手机, 严重怀疑其未来前景。不要总幻想嵌入式设备的跨平台,至少目前我认为这是痴心妄想。

 

 

4.   开发环境的搭建

移步到我在实践中得到的总结, 将更有效的帮助大家搭建一个有效的持续构建的OSGi项目. 链接地址


本章的以下内容错误和不确定的因素颇多, 不建议继续参考. 上面的链接将更有帮助。

 

说明一下, 这里的环境是最原始的搭建方式, 虽然用到Eclipse来编码, 但实际上各类配置文件, 都是通过工具生成, 而不是Eclipse RCP开发用的Wizard方式. 所以开发环境需要准备的几个大件为:

1.      Java和Eclipse环境. 用的是最普通的Eclipsejava开发. 非RCP开发. 这个没有必要说

2.      OSGi用到的SDK. 这里采用的是Apache的Felix OSGi实现(书中已经很清楚), 更正几个地方, 有些命令是不对的.

a)        HelloWorld的提示不对

b)        后面的命令不对,ps不知道是什么, 敲入help看帮助吧.

3.      产品发布时用的Bnd工具

4.      持续集成用到的ANT工具

 

最后发现, 除了源代码之后, 后面都基本上采用的是脚本方式处理.

 

通过bnd插件搭建工程的方式, 参考文档:

http://www.ralfebert.de/tutorials/osgi_server/create_bnd_project/

似乎这个书比较老, 插件更新太快, BntTools试用了一下, 至少下面这个是不行的: 在bnd文件上右键点击, 能看到Make Bundle这样的右键菜单项。Forget it! 我们这边还是玩命令行吧!

 

Bnd命令行环境搭建:

>> 这个哥郁闷了. 死活都找不到下载的文件.Dropbox被封, 所谓的google group也不怎么给力, 这里决定不用这个工具. 手动来进行打包发布。

Ant工具还是用来编译和持续集成好。

后来在stackoverflow中, 老外告知, 这个命令行还要自己封装。哥就郁闷了:

 

 

 

5.   重头戏出台:Bundle LifeCycle


文章说这个地方时最值得花时间的. 但目前还没有体会到。注意一下Resolved这个状态。处于这个条件下的Bundle意味着所有的限制条件/环境均已满足。OSGI这样的LifeCycle有利于做Incremental Development(增量开发).

 

 

1.   与系统交互

6.1 查找系统级别的配置属性

6.2通过id号找到另一个已经安装好的Bundle

6.3获得安装好的所有Bundle列表

通过BoundleContext方法getBundles(),  书中的代码

6.4检查和操控其他的Bundle

通过编程的方式, 检查和操控其他的Bundles, 比如启动、停止、卸载、更新Bundle等.

依然是: 通过BoundleContext方法getBundles(), 试试

 

6.5 安装新的Bundle.

通过编程的方式, 安装新的Bundle

 

6.6 持久化操作

框架系统管理的持久化存储区的的文件, 能对它们进行存取操作.

 

6.7 Bundle的监听操作

对Bundle注册一个侦听器,或者取消侦听器. 这些侦听器能告诉我们, 框架系统中的任何Bundles是否发生了变化, 书中List2.5似乎说的很明白了. 并且告知什么时候增加一个Listenner(start的时候, 将Listener编程进去)

 

6.8 Service的监听操作

对Service注册一个侦听器,或者取消侦听器。 这些侦听器能告诉我们,框架系统中的任何服务是否发生了变化。

6.8 框架系统的监听操作

对框架系统注册一个侦听器,或者取消侦听器。 这些侦听器能告诉我们,框架系统的某些事件在发生改变。

 

 

 

7.   延伸阅读

 

详解MANIFEST.MF文件

http://www.java3z.com/cwbwebhome/article/article2/2843.html

 

Jar Hell

http://tech-read.com/2009/01/13/what-is-jar-hell/

 

 

classLoader

 

http://haofenglemon.iteye.com/blog/426382

 





原文链接:http://blog.csdn.net/ostrichmyself/article/details/6604069
加载中
返回顶部
顶部