新書推薦:
《
画楼:《北洋画报》忆旧(年轮丛书)
》
售價:HK$
337.5
《
大国脊梁:漫画版
》
售價:HK$
80.2
《
跟着渤海小吏读历史:大唐气象(全三册)
》
售價:HK$
189.0
《
心智的构建:大脑如何创造我们的精神世界
》
售價:HK$
81.4
《
美国小史(揭秘“美国何以成为美国”,理解美国的经典入门读物)
》
售價:HK$
81.4
《
中国古代北方民族史丛书——东胡史
》
售價:HK$
87.8
《
巨人传(插图珍藏本)
》
售價:HK$
705.6
《
地下(村上春树沙林毒气事件的长篇纪实)
》
售價:HK$
76.7
編輯推薦:
OSGi实战是学习OSGi的全面指导,利用与架构和开发人员相关的示例清楚地讲解OSGi概念,同时探讨了很多实践场景和技术,阐述了开发人员有多需要OSGi,怎么将OSGi嵌入其他容器中,将遗留系统移入OSGi的最佳实践,等等。理论与实践相结合,能指导实际开发。
2.本书作者Richard S. Hall是Apache
Felix项目长期的代码提供者Sun公司GlassFish团队一员。
Karl Pauls也是Felix代码提供者。
Stuart
McCulloch是CodeDragons的Java开发人员,OSGi解决方案组的领导,Felix代码提供者。
內容簡介:
为了弥补OSGi规范在应用指导方面的不足,四位活跃在OSGi开发第一线的技术专家联手打造了《OSGi实战》。《OSGi实战》面向OSGi规范的使用者,系统、全面、深入地阐述OSGi的重要特性及其使用方法。《OSGi实战》还介绍了某些技术的低层实现细节,引领读者畅游OSGi的世界。
《OSGi实战》面向OSGi规范的使用者,通过精彩的讲解和贴近实战的丰富示例,帮助读者完成“入门-进阶-提高”三级跳。
關於作者:
Richard S. Hall是Apache Felix
框架开发组的一位活跃成员,该开发组和其他组一样都是Felix的子项目。2000年以后Richard参与了OSGi的开源工作并于2004年直接加入OSGi联盟。Richard是Apache软件组织的一位成员,目前在Oracle的GlassFish项目组工作,主要工作是帮助解决OSGi相关的问题或者从事一些其他他所擅长做的事情。
Karl Pauls实现了Apache Felix Framework Security Provider,他也是Apache
Felix框架开发组的一位活跃成员,该项目组和其他组一样都是Felix的子项目。Karl是Apache软件组织的一名成员,曾经参与了多个Apache以及其他的开源项目。Karl是Luminis的一位研究员。
Stuart Mcculloch主要负责Apache
Felix的maven-bundle插件以及OPS4j中用来快速开发OSGi的Pax-Construct工具。他还是Peaberry的作者,Peaberry是Guice的扩展用来实现对动态服务的注入。Stuart是Sonatype的顾问,主要负责依赖注入和模块化。
David
Savage在Paremus工作,2005年后他一直致力于OSGi应用的设计和构建,涵盖了众多领域如:构建工具、组件模型、数据持久化、桌面UI、管理方法、消息通信、配置、解析器以及RPC。借助Sigil子项目David重点在开发工具领域进行了深入研究,为Apache
Felix 项目做出了贡献。他同时还直接参与了OSGi联盟有关OSGi规范的制定工作。
目錄 :
第一部分 OSGi:模块化、生命周期和服务
第1章 揭开OSGi的面纱
1.1 OSGi的定义和目标
1.1.1 Java模块化的不足
1.1.2 OSGi能帮助你吗
1.2 OSGi架构概览
1.2.1 OSGi框架
1.2.2 将它们结合起来
1.3 Hello, world!
1.3.1 模块层示例
1.3.2 生命周期层示例
1.3.3 服务层示例
1.3.4 场景设置
1.4 OSGi的相关技术
1.4.1 Java EE
1.4.2 Jini
1.4.3 NetBeans
1.4.4 JMX
1.4.5 轻量级容器
1.4.6 Java 业务集成
1.4.7 JSR 277
1.4.8 JSR 294
1.4.9 SCA
1.4.10 .NET
1.5 小结
第2章 精通模块化
2.1 什么是模块化
2.2 为什么使用模块化
2.3 模块化绘图程序
2.4 bundle
2.4.1 bundle在物理模块化中扮演的角色
2.4.2 bundle在逻辑模块化中扮演的角色
2.5 使用元数据定义bundle
2.5.1 可读信息
2.5.2 bundle标识
2.5.3 代码可见性
2.5.4 类搜索顺序
2.6 完成绘图程序设计
2.6.1 提高绘图程序的模块化
2.6.2 启动新的绘图程序
2.7 OSGi依赖解析
2.7.1 自动解析依赖
2.7.2 使用约束保证一致性
2.8 回顾模块化绘图程序的好处
2.9 小结
第3章 生命周期
3.1 生命周期管理
3.1.1 什么是生命周期管理
3.1.2 为什么需要生命周期管理
3.2 OSGi bundle的生命周期
3.2.1 将生命周期引入绘图程序
3.2.2 OSGi框架在生命周期中的作用
3.2.3 bundles激活器的清单文件条目
3.2.4 生命周期API
3.2.5 生命周期状态图
3.2.6 bundle缓存和框架重启
3.3 在bundle中使用生命周期API
3.3.1 配置bundle
3.3.2 部署bundle
3.3.3 检查框架状态
3.3.4 持久化bundle状态
3.3.5 事件监听
3.3.6 bundle自我销毁
3.4 动态扩展绘图程序
3.5 生命周期与模块化
3.5.1 解析bundle
3.5.2 刷新bundle
3.5.3 当更新操作没有完成更新
3.6 小结
第4章 学习服务
4.1 什么是服务、为什么使用服务、什么时候用服务
4.1.1 什么是服务
4.1.2 为什么使用服务
4.1.3 什么时候应该使用服务
4.1.4 什么时候不应该使用服务
4.1.5 仍然不确定
4.2 OSGi服务实战
4.2.1 发布服务
4.2.2 查找并绑定服务
4.3 处理动态性
4.3.1 避免常见的陷阱
4.3.2 监听服务
4.3.3 服务跟踪
4.4 在绘图示例中使用服务
4.4.1 定义图形服务
4.4.2 发布图形服务
4.4.3 跟踪图形服务
4.5 将服务关联到模块层和生命周期层
4.5.1 为什么不能看到我的服务
4.5.2 能否提供一个bundle特有的服务
4.5.3 应该在何时释放服务
4.5.4 何时注销服务
4.5.5 应该将接口分开打包吗
4.6 标准服务
4.6.1 核心服务
4.6.2 compendium服务
4.7 小结
第5章 深入分析模块化特性
5.1 管理导出
5.1.1 导入导出包
5.1.2 隐式导出属性
5.1.3 强制导出属性
5.1.4 导出过滤
5.1.5 复制导出
5.2 导入解耦
5.2.1 可选导入
5.2.2 动态导入
5.2.3 可选导入与动态导入的比较
5.2.4 日志示例
5.3 需要的bundle
5.3.1 声明bundle依赖关系
5.3.2 聚合分割包
5.3.3 bundle依赖的相关问题
5.4 将bundle划分为片段
5.4.1 片段
5.4.2 本地化中使用片段
5.5 处理与环境相关的问题
5.5.1 依赖执行环境
5.5.2 构建本地库
5.6 小结
第二部分 OSGi实践
第6章 走近bundle
6.1 将JAR转换成bundle
6.1.1 选取bundle标识
6.1.2 导出包
6.1.3 发现需要导入的包
6.1.4 嵌入与导入
6.1.5 增加对生命周期的支持
6.1.6 JAR文件转换为bundle的简要说明
6.2 分割一个应用到多个bundle
6.2.1 创建一个大型bundle
6.2.2 将代码拆分到多个bundle中
6.2.3 降低模块耦合
6.2.4 是否要转换成bundle
6.3 小结
第7章 测试应用程序
7.1 迁移测试到OSGi
7.1.1 容器内测试
7.1.2 创建测试bundle
7.1.3 覆盖所有基础
7.2 模拟OSGi
7.2.1 测试期望的行为
7.2.2 模拟实战
7.2.3 模拟意外情景
7.2.4 处理多线程测试
7.2.5 暴露竞态条件
7.3 OSGi高级测试
7.3.1 OSGi测试工具
7.3.2 在多个框架中运行测试
7.3.3 单元测试
7.3.4 集成测试
7.3.5 管理测试
7.4 小结
第8章 调试应用
8.1 调试bundle
8.1.1 调试实战
8.1.2 使用HotSwap解决问题
8.2 解决类加载相关问题
8.2.1 ClassNotFoundException与NoClassDefFoundError
8.2.2 类型转换问题
8.2.3 使用uses约束
8.2.4 同Class.forName划清界限
8.2.5 线程上下文类加载器
8.3 追踪内存泄漏
8.4 悬挂服务
8.4.1 查找悬挂服务
8.4.2 防止悬挂服务
8.5 小结
第9章 管理bundle
9.1 包和bundle的版本控制
9.1.1 有效的版本控制
9.1.2 包的版本控制
9.1.3 bundle的版本控制
9.2 配置bundle
9.2.1 配置管理服务
9.2.2 元类型服务
9.2.3 首选项服务
9.3 延迟启动bundle
9.3.1 激活策略
9.3.2 使用激活策略
9.4 小结
第10章 管理应用
10.1 部署bundle
10.1.1 管理代理
10.1.2 OSGi bundle仓库
10.1.3 部署管理服务
10.2 指定bundle激活顺序
10.2.1 介绍启动级别服务
10.2.2 使用启动级别服务
10.3 小结
第三部分 高级主题
第11章 组件模型和框架
11.1 面向组件
11.1.1 什么是组件
11.1.2 为什么需要组件
11.2 OSGi与组件
11.2.1 OSGi面向服务的组件模型
11.2.2 改进OSGi组件模型
11.2.3 使用组件的绘图示例
11.3 声明式服务
11.3.1 构建声明式服务组件
11.3.2 使用声明式服务提供服务
11.3.3 利用声明式服务使用服务
11.3.4 声明式服务组件生命周期
11.4 小结
第12章 高级组件框架
12.1 Blueprint容器
12.1.1 Blueprint架构
12.1.2 用Blueprint提供服务
12.1.3 通过Blueprint使用服务
12.1.4 Blueprint组件生命周期
12.1.5 Blueprint高级特性
12.2 Apache Felix iPOJO
12.2.1 构建iPOJO组件
12.2.2 通过iPOJO提供服务
12.2.3 通过iPOJO使用服务
12.2.4 iPOJO组件生命周期
12.2.5 使用iPOJO实例化组件
12.3 混合和匹配
12.4 小结
第13章 启动和嵌入OSGi框架
13.1 标准启动和嵌入
13.1.1 框架API概览
13.1.2 创建框架实例
13.1.3 配置框架
13.1.4 启动框架实例
13.1.5 停止框架实例
13.2 启动框架
13.2.1 确定安装哪些bundle
13.2.2 干净地关闭
13.2.3 配置、创建和启动框架
13.2.4 安装bundle
13.2.5 启动bundle
13.2.6 启动主bundle
13.2.7 等待关闭
13.3 嵌入框架
13.3.1 内部还是外部
13.3.2 谁在控制
13.3.3 嵌入式框架示例
13.4 小结
第14章 确保应用程序的安全
14.1 使用安全或者不使用安全
14.2 安全:努力尝试使用
14.3 OSGi特定的权限
14.3.1 PackagePermission
14.3.2 BundlePermission
14.3.3 AdminPermission
14.3.4 ServicePermission
14.3.5 相对文件权限
14.4 使用条件权限管理服务管理权限
14.4.1 条件权限
14.4.2 条件权限管理服务
14.4.3 bundle 位置条件
14.4.4 使用Conditional-Permission Admin
14.4.5 实现一个策略文件读取器
14.5 数字签名的bundle
14.5.1 学习术语
14.5.2 创建证书和签名bundle
14.5.3 BundleSignerCondition
14.6 本地权限
14.7 高级权限管理
14.7.1 自定义条件概览
14.7.2 基于日期的条件
14.7.3 用户输入条件
14.8 汇总
14.9 小结
第15章 Web应用和Web服务
15.1 创建Web应用
15.1.1 使用HTTP服务规范
15.1.2 使用Web应用规范
15.1.3 标准WAR:Web URL处理程序
15.2 提供和使用Web服务
15.2.1 提供一个Web服务
15.2.2 使用Web服务
15.2.3 发布服务
15.3 小结
附录A 构建 bundle
附录B OSGi标准服务
內容試閱 :
序
初闻Richard S. Hall是在2003年炎热的夏天。在咖啡桌上,德国电信(Deutsche
Telekom)的一位同事告诉我,当地大学的一位老师对OSGi非常了解。首批开源的OSGi框架中,Oscar的作者便是他。在2003年,全方位地采用OSGi实属罕见,所以当时我很好奇。与此同时,Eclipse项目正着手研究采用一套新的模块化系统,我受邀以OSGi专家的身份参与了该项目。对于该项目来说,我觉得Richard能提供很大帮助,所以邀请他加入Equinox委员会。谁料这个颇具意外的邀请拉开了此后一个漫长的邮件交往历程的序幕,这种邮件往来一直延续至今,我希望永远都不要结束。每每遇到规范不够清晰,甚至更糟——当我们试图违背模块化原则时,Richard总会变得怒不可遏。如果我们不得不接受不完善的功能,有时我会觉得他身受煎熬。作为一名受邀的OSGi研究人员,他已然成为规范背后的核心人物,为我们的设计保驾护航,避免框架膨胀,并且一直遵照我们的原则。
Manning出版社向OSGi的核心人员发出了一封邀请函,提议编写本书,当时Richard也收到了该邮件。这封邮件引发了后续关于集体撰写书籍的一系列热烈讨论。之前写书的想法已经反复讨论过。对此我们也与Manning出版社进行了洽谈,但最后我选择退出,并鼓励其他同事继续参与下去。我为何会选择退出?作为OSGi规范的编写者,我很清楚和其他固执己见的人共同撰写一本书需要多大的工作量。我不希望每天晚上以及周末都加班工作,而且没有相应的报酬,虽然我非常喜欢和欣赏这些有才华的同事。很遗憾,我的决定影响了大家,这一工作被搁置了。
直到有一天Richard告诉我他已经重新拾写书计划,打算继续完成之前的工作,目前已经组建了一支更好的团队,有Karl
Pauls、Stuart McCulloch和 David Savage。每一位作者都为开源和OSGi
规范做出过巨大贡献。Karl在使用Felix方面经验丰富,通过将Felix的安全划分为一个单独的bundle,以此来证明模块化,Karl证明了Felix的框架结构也是模块化的。Stuart在Maven
bundle插件、Ops4J以及Peaberry对Guice的扩展等方面做了很多工作。David在Apache的Sigil项目上干得很出色,在Paremus公司也取得了不错的业绩。最了解如何在实践中使用OSGi的人全在这支团队里了。作者将所有经验都融入了本书,这本书令人叹为观止。
这支团队承担了撰写此书的艰巨任务,在撰写过程中我一直和他们保持着密切联系——这不仅是因为我们都在OSGi联盟共事,还有一个重要的原因:在创作一本介绍OSGi的著作过程中,很可能会发现OSGi规范的缺点和不足,很明显这又会引发新一轮的激烈讨论,讨论通过Skype或者电子邮件进行。很遗憾,大多数情况下讨论都以该团队的获胜而告终。
他们请我写些文字介绍OSGi的历史,通过最大限度地压缩,最终仅仅用了4536个字,我想这就是OSGi。这正是我想要的:对高质量的追求,不仅体现在书中细节方面,还体现在组织形式上。不同于目前市面上的其他书,本书并没有使用大量的代码清单,概括出几个步骤从而实现某个预期结果。恰恰相反,其撰写方式我很喜欢:不仅介绍使用OSGi的细节,更不厌其烦介绍这其中的缘由,可谓一部释理之书。
时下正需要这样一本书。我很清楚搞懂OSGi实非易事。尽管OSGi是在面向对象的基础上构建起来的,但是它增加了一系列新的设计原语,用以阐释传统面向对象设计的一些缺点。当应用规模变大,需要用到一系列开源项目或第三方代码时,这些缺点就会表现出来。在软件构建过程中,面向对象仍可算是一项颇具价值的技术。但是面向对象模式不适用于大规模的构建块(组件)一起协同工作同时又不产生更多耦合的情况。我们拼命地使用工厂、类加载黑客等模式降低对象间的耦合,但是当达到一定程度时,降低耦合度的工作会占用我们大部分精力。依赖注入降低了代码耦合,但是大量代码需要使用XML编写,XML语言本身的语法在编程领域不是很合适。注解为处理耦合提供了另一程度的支持,但使用注解本身又会带来一系列问题。在降低耦合方面我们的处理方式大都是在做表面文章,因为在传统Java运行时环境下,这些边界并未清晰界定。
OSGi不同于传统Java。它将应用视为对等模块的相互协作:模块会主动适应环境而不是假设环境会主动适应模块。适应环境需要该环境真实存在,然而这正是OSGi最大的创新之处:μServices。μServices是模块间相互联系的纽带,它允许模块随时间不断演化而不会影响其他模块。在最近的一次OSGi社区交流中,David
Savage使用spiky(尖刻、易怒的)一词来描述模块,用以说明一组模块之间的耦合会使修改模块变得非常困难。μServices是OSGi中的一个设计原语,它很强大,甚至可以在应用运行的情况下动态完成对模块的更新或者安装操作。通过将模块间的交互具体化,减缓模块间的冲突。
μServices是一个崭新的概念,理解它需要换一个角度思考,这不同于时下盛行的Java。在很多方面,OSGi很像25年前的面向对象程序设计,它提供了一些新的设计原语,而这可能不同于时下的一些主流思路。面向对象需要注入新的活力,这包括一些新的设计原语,比如多态、继承、类和对象。OSGi使用bundle和μServices缔造了一些新的概念。我相信这些设计原语会成为继面向对象之后的下一代软件设计概念。这本书非常优秀,借助它你能够真正地理解OSGi,并了解它的所有好处。
Peter Kriens
OSGi 技术总监
揭开OSGi的面纱
本章内容
理解Java对模块化的内置支持
介绍OSGi技术及其如何增强Java的模块化
与其他技术相比OSGi的定位
Java平台获得了巨大的成功。小到移动设备,大到企业应用,Java已被广泛用于开发各种应用程序,从而证明了它那经过深思熟虑的设计和持续不断的演进是成功的。但在Java的成功之下,除了支持普通的面向对象的数据封装之外,却并没有明确地支持构建模块化系统。
这对于你来说意味着什么?Java缺少对高级模块化的支持,如果仍然说它是一种成功,那么你可能想知道缺少这种支持是否会引起问题。为了弥补Java在模块化方面的不足,大多数管理得当的项目都必须针对具体项目要求去建立一整套技术。这些技术可能会包括如下方面。
适应逻辑结构的编程实践。
多个类加载器的技巧。
进程内部组件间的序列化。
但是这些技术在编译时或执行时没有强制执行检查,因此本质上是脆弱的而且容易出错,从而最终会对应用程序生命周期的多个阶段造成不利的影响。
开发——无法清楚、明确地将开发划分为独立的部分。
部署——很难分析、理解并解决某些需求,这些需求来自一个完整系统的各个独立部分。
执行——无法管理和改进一个正在运行的系统的各个组成部分,更不用说减小因管理和改进带来的影响。
这些问题都是Java可以解决的,并且很多项目也都通过使用上文提到的一些定制技术实现了这一点,但是这些方法都比较复杂。为了避开基础功能的不足,我们异常纠结。如果Java对模块化有明确的支持,那么你就可以不再被这些问题困扰,专注于你真正想做的——全力开发应用程序的功能。
欢迎进入OSGi服务平台。OSGi服务平台是专门针对Java对模块化支持不足的情况,由OSGi联盟定义的一个行业标准。作为其对模块化支持的延续,为了清晰地分离接口和实现,OSGi服务平台引入了一个面向服务的编程模型,被某些人称作“VM中的SOA”。本章将概述OSGi服务平台以及利用它如何使用基于接口的开发模型,创建模块化的、可管理的应用。
学习完本章后,你将了解OSGi技术在众多Java技术中所扮演的角色,了解为什么Java和其他与Java相关的技术没有OSGi技术所提供的具体功能。
1.1 OSGi的定义和目标
“OSGi是什么?”这个问题非常难回答。最简单的答案是:OSGi
是Java平台的一个模块化层。当然,接下来的问题是:“模块化是什么?”这里使用模块化或多或少是从传统计算机科学意义上来说的。在计算机科学里,软件应用程序的代码被分割为表示独立内容的逻辑单元,如图1-1所示。如果软件是模块化的,你可以简化开发,并且通过强化逻辑模块的界限来提高可维护性。第2章讨论更多模块化的细节。
图1-1 模块化是指将一个大系统从逻辑上分解为较小的互相协作的部分
模块化不是新概念,早在20世纪70年代就开始流行了。OSGi技术正在遍地开花,例如,它已经被作为Eclipse
IDE和GlassFish应用服务器的运行环境。为什么现在它越来越流行呢?为了更好地理解OSGi为什么越来越重要,有必要先了解一些Java在创建模块化程序时的不足。了解了这些不足,你就能明白OSGi技术为何如此重要,以及它如何帮助你。
1.1.1 Java模块化的不足
Java
以面向对象的方式提供了某种程度的模块化,但它从未考虑支持粗粒度的模块化编程。尽管因为某些Java不想去解决的问题而批评它有失公允,但是Java的成功确实给那些需要更好的模块化支持的开发者带来了困难。
Java已经发展成为构建不同领域的各类应用程序的平台,范围从移动电话到企业应用。大多数应用都需要更广泛的模块化支持,或者至少应从模块化中受益。下面就来看一下Java模块化的不足。
低层代码的可见性控制
虽然Java提供了很多控制可见性的访问修饰符(例如public、protected、private和包级私有),但这些都是为了解决低层面向对象封装,而不是逻辑系统划分。Java用包来划分代码。如果需要代码在多个包之间可见,那么包内的代码须声明为public(或在继承时声明为protected)。有时应用程序的逻辑结构需要特定的代码分属于不同的包,但是这也意味着任何包间依赖必须声明为public,从而让代码都可见。通常这样会暴露具体的实现细节,使后续的升级更加困难,因为用户可能已经依赖于未公开的API。
代码清单1-1以 “Hello
world!”程序为例进行说明:一个包提供公开接口,另一个包提供该接口的私有实现,第三个包编写主类。
代码清单1-1 Java面向对象封装的限制
这段代码的作者可能打算只允许第三方程序通过Greeting接口与该程序交互。他们在Java文档、技术教程、博客或者邮件中可能都会提及这个要求,但却无法阻止第三方使用公有构造函数构造一个新的GreetingImpl,正如所完成的逻辑。
你或许会说,构造函数本不应该是公有的,也不需要把程序分割到多个包中。对于这个小例子来说,这当然是没问题的。但是,在实际应用中,类级的可见性加包在保证API的一致性时略显拙劣。如果私有实现细节可以被第三方开发者访问,那么当进行升级时,你除了考虑公有接口之外,还需要关注私有实现的变化。
这个问题的根源在于,虽然Java包表面上是通过包嵌套而使其具有逻辑关系,但其实不然。人们初学Java时通常有一个误解,认为父子关系的包被赋予了特殊的可见性。其实存在嵌套关系的两个包与两个没有嵌套关系的包是相同的。包嵌套主要是为了避免命名冲突,而仅为逻辑代码划分提供了部分支持。
所有这些表明在Java语言中,你通常必须要面对下面两种选择。
1 为了避免暴露非公有API而把不相关的类打包在一起,以致损害程序的逻辑结构。
2 为了保持程序逻辑结构而使用多个包,代价就是暴露非公有API,被不同包中的类访问。
两种选择都不尽如人意。
易错的类路径概念
Java平台也会妨碍好的模块化实践,罪魁祸首正是类路径(class
path)。为什么类路径会为模块化带来困难呢?最大的原因是类路径隐藏了代码版本、依赖和一致性等特性。应用程序一般由各种版本的库和组件组成。类路径不关心代码版本——只返回找到的第一个版本。即使关注代码版本,但还是无法明确描述依赖关系。建立类路径的过程是烦琐易错的,你只是不停地添加类库,直到虚拟机不再报出找不到类的错误。
图1-2表明当多个JAR文件提供指定类集的时候
“类路径困境”问题会经常发生。即使每个JAR文件已经按照一个工作单元完成编译,但当在执行时合并时,Java
类路径并不关心组件的逻辑划分。这就会导致难以预料的错误,例如:一个JAR文件的类与另一个JAR文件中不兼容的类交互时会出现NoSuchMethodError异常。
图1-2 按照在类路径中出现的顺序来合并多个包含重叠类或包的JAR文件,而没有考虑包的一致性
在由独立开发的组件构成的大型应用中,依赖同一组件不同版本的情况并不少见,例如日志或者XML解析。而类路径会强制选择某个可能并不合适的版本。更糟的是,如果类路径中有同一个包的多个版本,不论是有意或是偶然,都会被Java当做是不同的包,并按照出现的顺序隐式地融合起来。
综上所述,类路径的方式不包含任何形式的一致性检查。无论获得的是系统管理员授权使用的哪些可用类,可能都只是接近你的预期。
部署和管理支持上的不足
Java还缺少对应用部署和管理的支持。在Java中存在对多个版本的依赖时,没有简单的方法来正确部署这些代码并执行。在部署之后更新应用和组件也会面临同样问题。
考虑一下希望支持动态插件机制的常见需求。唯一能实现这个合理需求的方法就是使用类加载器,但是这种方法是低级的,且容易出错。类加载器并不是应用开发者的常用工具,但是现今的许多系统却必须使用它。一个合理定义的Java模块层能够通过明确模块定义和提高代码划分的抽象级别,提供对这些特性的支持。
在更好地理解Java模块化不足的基础上,我们可以思考一下OSGi是否是你的项目的理想解决方案。
1.1.2 OSGi能帮助你吗
除了最简单的应用程序外,几乎所有的应用程序都能够获益于OSGi提供的模块化特征,所以如果你正对是否应该了解OSGi犹豫不决,那么答案非常可能是肯定的。仍然不相信吗?在以下这些你可能遇到过的常见情况下,OSGi恰恰能够给你提供一些帮助。
当启动程序时,由于类路径不正确而导致ClassNotFoundException异常。OSGi可以帮助你先确保代码满足依赖关系,然后才允许执行代码。
由于类路径上一个依赖库的错误版本而导致程序执行时错误。OSGi会在要求的版本和其他约束条件方面对依赖集进行一致性检查。
当模块间共享类时导致类型不一致,更加具体地说,出现了像foo instanceof Foo
==false这样令人生畏的问题。使用OSGi,你就不必担心由于层次化的类加载模式隐含的限制。
将一个程序打包成逻辑上独立的JAR文件,并且只部署那些某个安装所需要的部分。这大致上阐述了OSGi的目的。
将一个程序打包成逻辑上独立的JAR文件,声明哪些代码可以被其他JAR文件访问,并且强调可见性。OSGi为JAR文件提供了新一级的代码可见性,这样你就可以指定哪些对外界是可见的,而哪些是不可见的。
为程序定义一个插件式的扩展机制。OSGi模块化特别适合提供强大的扩展性机制,包括支持执行时的动态性。
正如你所见,这些场景覆盖了很多用例,我们无法一一列举。OSGi简单而非侵入式的本质使得你用得越多,就发现使用它的方式越多。上文已经展示了标准Java
类路径的一些不足,现在我们该向你介绍OSGi了。
1.2 OSGi架构概览
OSGi服务平台由两部分组成:OSGi框架和OSGi标准服务(如图1-3所示)。OSGi框架是实现并提供OSGi功能的运行环境,OSGi标准服务定义了很多用于执行常见任务(如日志和首选项)的可重用API。
图1-3 OSGi服务平台规范分为两部分:OSGi框架和标准服务
OSGi框架和标准服务的规范由OSGi联盟(www.osgi.org)管理。OSGi联盟成立于1999年3月,是一个行业支持的非营利性组织。目前的OSGi框架规范是第4个版本,该版本是稳定的。基于该规范的技术已经应用于一系列大规模的行业应用,包括(但不限于)汽车、移动设备、桌面应用以及近来的企业应用服务器。
注意 OSGi曾经是Open Services Gateway
Initiative(开放服务网关协议)的首字母缩写。该缩
写词突出了技术的传承,但是已经过时。第3版规范发布以后,OSGi联盟正式废弃了这个缩写,现在OSGi只是该技术的商标。
本书大部分内容都将讨论OSGi框架、OSGi框架的功能以及如何使用这些功能。因为OSGi框架提供了大量的标准服务,我们将在恰当的时候只讨论最相关和最有用的服务。对于未涉及的服务,你可以从OSGi规范中获得更多的信息。现在,我们将通过介绍OSGi框架的主要特点来继续介绍OSGi。
1.2.1 OSGi框架
OSGi框架在创建基于OSGi的应用时起着核心作用,因为它是应用的执行环境。OSGi联盟在OSGi框架规范中定义了框架的正确行为,这样就可以基于一个定义清晰的API进行编程。该规范也使核心框架可以有多种实现方式,从而可让你自由选择。此外,还有一些知名的开源项目,比如Apache
Felix(http:felix.apache.org)、Eclipse
Equinox(www.eclipse.orgequinox),以及
Knopflerfish(www.knopflerfish.org)。这最终会给你带来好处,因为你不需要受限于特定的供应商,而只需根据规范中定义的行为编程即可。这种感觉令人欣慰,就好比你知道进入世界上任何地方的麦当劳都可以买到同样的饭。
OSGi技术开始被应用到各个地方。你或许不知道,使用IDE做Java开发时,很可能你就已经用过OSGi了。Equinox
OSGi框架实现是Eclipse IDE的底层运行时环境。此外,如果你使用GlassFish
v3应用服务器,其实你也在使用OSGi,因为Apache Felix
OSGi框架实现是它的运行时环境。多种应用场景表明OSGi框架具有很高的价值和灵活性,而这个框架是依据OSGi规范中定义的三个概念层设计的(如图1-4所示)。
模块层——关注于打包和共享代码。
生命周期层——关注于提供执行时模块管理和对底层OSGi框架的访问。
服务层——关注于模块,特别是模块内的组件间的交互和通信。
图1-4 OSGi分层架构
像常见的分层架构一样,OSGi的每一层都依赖于它的下层。因此,你可以只使用OSGi的下层而不用它的上层,但反之则不然。接下来的三章将会详细地讨论这三层,在这里我们将概述每一层。
模块层
模块层定义了OSGi模块的概念,并将之称为一个bundle。bundle是一个包含元数据(关于数据的数据)的JAR文件,由类文件和相关资源组成,如图1-5所示。bundle通常并不是打包到一个JAR文件中的整个应用程序;相反,它们是构成一个特定应用程序的多个逻辑模块。bundle比标准的JAR文件更强大,因为你可以明确地声明哪些包对外可见(即导出包)。从这个意义上说,bundle扩展了Java的普通访问修饰符(public、private和protected)。
图1-5 bundle中包括代码、资源和元数据
相比标准JAR文件,bundle的另一个重要优势是你可以明确声明依赖哪些外部包(即导入包)。明确声明bundle的导入包和导出包的主要好处是,OSGi框架可以自动地管理和验证它们的一致性;这个过程称为bundle解析,包括使导出包与导入包相匹配。bundle解析确保bundle版本和其他方面约束的一致性,我们将在第2章详细介绍。
生命周期层
生命周期层定义了在OSGi框架中是如何动态安装和管理来的。这好比你建造一座房子,模块层是基础和结构,而生命周期层是供电线路,它使得房子里所有的东西运转起来。
生命周期层的存在有两个目的。一方面,在应用程序的外部,生命周期层精确地定义了bundle生命周期的操作(安装、更新、启动、停止和卸载)。这些生命周期的操作使得你可以用一种定义明确的方式动态地提供、管理和改进你的应用程序。这意味着可以安全地在框架中安装和卸载bundle,而不需要重启应用进程。
另一方面,在应用程序的内部,生命周期层定义了bundle如何访问它们的执行环境。执行环境为bundle提供了一种与OSGi框架交互的方式和执行时的一些便利。生命周期层整体的方法非常强大,支持创建可从外部(和远程)管理的应用程序,或者完全自我管理的应用程序(或者两者的任意组合)。
服务层
最后,服务层支持和促成了一个灵活的应用编程模型。该模型包含了一些因面向服务的计算而流行起来的概念(尽管面向服务的计算是在这些概念已经成为OSGi框架的一部分后才开始流行起来的)。主要的概念涉及面向服务的发布、查找和绑定交互模式:服务提供者将服务发布到服务注册中心,然后服务客户端通过搜索服务注册中心,查找可供使用的服务(如图1-6所示)。当今,这种面向服务的架构(SOA)大部分都与Web
服务有关;但是OSGi服务则属于VM的一部分,因此有些人把它称为VM中的SOA。
图1-6 面向服务的交互模式。服务提供者将服务发布到注册中心,然后服务请求者
可以从注册中心那里看到可供使用的服务
OSGi服务层是符合直觉的,因为它提倡一种基于接口的开发方式,该方式是一种公认的优秀编程实践。确切地说,它提倡接口与实现之间的分离。OSGi服务是Java接口,表示服务提供者和服务客户端之间的一种概念上的合约。服务层于是很轻量,因为服务提供者只是一些通过直接的方法调用来访问的Java对象。另外,服务层通过基于服务的动态性(服务可以在任何时刻出现或消失)来扩展生命周期层基于bundle的动态性。结果是产生了一种支持模块化和灵活性的编程模型,避免了过去僵化又脆弱的方式。
这听起来还算不错,那如何将这三层组合起来,如何使用它们,如何在它们之上创建应用程序呢?在接下来的两章将利用一些小的示例来说明如何将这些层组合起来。
1.2.2 将它们结合起来
OSGi框架由多层组成,但如何在应用程序开发中使用这些层呢?为了说明得更清楚,我们先介绍以下创建OSGi应用程序的通用方法。
1 设计应用,将它分解为一些服务接口(普通的基于接口的编程)和这些接口的客户端。
2 使用你选定的工具和方法来实现服务提供者和客户端组件。
3 将服务提供者和客户端组件打包为独立的JAR文件(通常要这样做),然后用合适的OSGi元数据扩展每个JAR文件。
4 启动OSGi框架。
5 安装和启动所有来自步骤3的JAR文件。
如果你已经采用了基于接口的方式,那么就会对OSGi的方式感到熟悉。主要的不同是如何找到你的接口实现(也就是服务)。通常,可以实例化实现,传递引用以初始化客户端。在OSGi中,服务会将它们自身发布到服务注册中心,然后你的客户端从服务注册中心寻找可使用的服务。那些bundle安装和启动之后,应用将正常地启动和执行,但却具有几个优点。应用底层的OSGi框架提供更严格的模块化和一致性检查,并且它的动态本质会展现多种可能性。
如果你不想或者不能使用基于接口的方法来开发,也不要因此而焦虑。OSGi框架的前两层仍然提供了很多的功能;事实上,OSGi框架的大部分功能都位于这两层中,所以请继续往下看。不如来看一些代码吧。
1.3 Hello, world!
由于OSGi的功能分为之前所述的三层(模块层、生命周期层和服务层),因此我们将展示三个不同的“Hello,
world!”示例来阐明每一层的功能。
1.3.1 模块层示例
模块层并不涉及代码的开发,而是关注于将你的代码打包成bundle。在开发过程中,你需要了解那些和代码相关的问题,但总的来说,准备模块层代码需要向项目生成的JAR文件中添加打包元数据。例如,假设你想共享下面这个类。
在构建的过程中,需要编译源代码并将所产生的类文件放入JAR文件中。为了使用OSGi模块层的功能,必须在JAR文件中的META-INFMANIFEST.MF文件中增加一些元数据信息,如代码清单1-2所示。
代码清单1-2 基本的Greeting实现
第一行指明了OSGi元数据的语法版本。第二行指定一个可读的bundle名称,此名称并未严格限制必须设置。第三行和第四行分别是bundle的符号名和版本号标识符。最后一行指明与其他bundle共享的包。
在本例中,大多数元数据都用于标识bundle。重要的部分是Export-Package声明,因为通过该声明可以扩展普通JAR文件的功能,你可以明确声明JAR文件中哪些包对使用者是可见的。本例中只有org.foo.hello包的内容是对外可见的;即使JAR文件内还包括其他的包,这些包对外也将是不可见的。这意味着,你在运行应用程序时,其他模块将不会有意或无意地依赖于你的模块中没有明确公开的包。
使用其他模块所提供的共享代码,也要通过在JAR文件中添加元数据来实现。此时,你需要使用Import-Package语句明确声明客户端JAR文件中的代码所需要的外部包。下面是代码片段:
本例中,最后一行指定了所依赖的一个外部包。
欲看该实例的运行效果,请到本书配套源码中的chapter01greeting-examplemodularity目录下,输入ant进行构建,并使用java
–jar main.jar命令来运行该实例。尽管这个实例很简单,但它说明了基于已有的JAR文件构建OSGi
bundle是一个合理的非侵入式过程。另外,还有一些工具可以帮助你生成bundle的元数据,我们将在附录A中进行说明;然而,实际开发中,除了平常用来创建JAR文件的工具外,我们并不需要什么特殊的工具来创建bundle。第2章将会针对OSGi模块化的有趣细节进行深入讨论。
1.3.2 生命周期层示例
在上一小节中,你可以看到利用非侵入的方法向已有的JAR文件中添加元数据可以发挥OSGi功能上的优势。这种简单的方法基本上满足了大部分库的重用需求,但有时你需要或是想要进一步地满足一些特殊需求,或是想要应用OSGi的一些更高级特性。生命周期层将会带你走入更深的OSGi世界。
也许你想要创建一个模块来执行一些初始化任务,例如启动一个后台线程或者初始化一个驱动程序;在生命周期层可以做到这一点。bundle中可以将一个指定的类声明为激活器(activator),作为该bundle进行自身生命周期管理的钩子。虽然第3章将对bundle的整个生命周期进行讨论,但首先还是通过一个简单的例子来初步了解一下bundle的生命周期。代码清单1-3将之前的Greeting类扩展为一个单例实例的类。
代码清单1-3 扩展的greeting实现
代码清单1-4实现了一个bundle的激活器,在bundle启动时,用来初始化Greeting类的单例,bundle停止时,用以清除该单例。使用者现在可以使用预先配置好的单例,而不再需要创建自己的实例。
bundle的激活器类必须实现一个简单的OSGi接口,本例中该接口由start和stop两个方法组成。在执行过程中,bundle启动时OSGi框架将构造该类的一个实例,并调用start方法,bundle停止时框架将调用stop方法。(这里所说的有关bundle的启动和停止,将在第3章详细地说明。)由于bundle处于运行状态期间,框架所使用的是该bundle的同一个激活器实例,因此可以在start方法和stop方法之间共享其成员变量。
你也许想要知道在start方法和stop方法中的BundleContext类型的参数有什么作用。该参数是bundle在运行过程中访问OSGi框架的桥梁。正因为有了该上下文对象,模块才可以使用OSGi模块层、生命周期层和服务层的所有功能。总之,对于大多数bundle来说,该对象都相当重要,我们将在稍后讨论生命周期层时再对该对象进行详细介绍。上述例子的重点在于,bundle可以通过简单的方法来控制它们的生命周期层,并且可以访问底层OSGi框架。
代码清单1-4 greeting实现的OSGi bundle激活器
当然,只创建一个bundle激活器实现类是不够的,你还要将这件事情通知给OSGi框架。幸好这很简单。如果你是在把一个已有的JAR文件转化成模块,那么必须向原有工程中添加激活器的实现类,因此,该实现类也应包含在JAR文件中。如果你是从头开始创建一个bundle,那么需要编译该激活器实现类,并且将编译结果放到JAR文件中。你还要在JAR文件的清单文件中添加额外的元数据,告诉OSGi框架bundle激活器类的位置。在本节的例子中,要在JAR清单文件中加入如下元数据:
注意,你还需要导入org.osgi.framework包,因为bundle
激活器依赖于该包。欲看该实例的运行效果,请到本书配套源码中的chapter01greeting-examplelifecycle目录下,输入ant进行构建,并使用java
–jar main.jar命令来运行该实例。
我们已经介绍了如何使用模块层,利用已有的JAR文件创建OSGi
bundle,以及通过使bundle在其生命周期可见,来使用框架的功能。本节的最后一个例子展示了由OSGi提倡的面向服务的编程方法。
1.3.3 服务层示例
如果你在开发中使用面向接口的开发方式,那么你肯定会习惯OSGi面向服务的开发方式。下面用Greeting接口的例子来说明:
对于任何给定的Greeting接口的实现,当sayHello方法被调用时,将会显示问候语。通常,服务表示服务提供者与未来客户之间的一种契约;如同规范一样,契约的语义常常用独立的可读文档来描述。前面的服务接口示例表示所有Greeting实现的语法契约。契约的概念是必须的,这样能够确保所有客户在使用Greeting服务时都能得到期望的功能。
客户端并不了解提供Greeting服务的具体实现细节。例如,一个实现可能会以文本方式打印问候语,而另一个实现可能在一个GUI对话框中显示问候语。代码清单1-5描述了一个简单的基于文本的实现。
代码清单1-5 Greeting接口的实现
你可能会这样想:服务接口或代码清单1-5中并没有任何东西表明你正在定义一个OSGi服务啊。确实是这样的。这就是上文说如果你使用过面向接口的开发方式,那么肯定会习惯OSGi面向服务的开发方法的原因,你的大部分代码都将与原来的代码相同。开发方法将有两处微小的不同:一是如何使服务的实例在应用程序的其他部分可用;二是如何使应用程序的其他部分发现这个可用的服务。
所有服务的实现最终都被打包到一个bundle中,且该bundle必须能够在其生命周期可见,以使其能够注册成服务。这意味着你需要为示例服务创建一个bundle激活器,如代码清单1-6所示。
代码清单1-6 用OSGi bundle激活器完成服务注册
这时,在start方法中使用OSGi提供的bundle上下文,将Greeting的实现在服务注册中心注册为服务,而不是将其存储为一个单例。需要提供的第一个参数是服务实现的接口名;第二个参数是实际的服务实例;第三个参数是服务属性。在stop方法中,你能够在bundle停止前将服务的实现注销;但是在实际使用过程中不需要这样做。当bundle停止时,OSGi框架会自动注销所有该bundle已注册的服务。
你已经知道如何注册服务,但是如何发现这个服务呢?代码清单1-7是一个简单的客户端程序,但是它没有处理服务缺失以及潜在的竞态条件的机制。第4章将会讨论一种更健壮的访问服务的方法。
代码清单1-7 用 OSGi bundle激活器完成服务发现
注意,访问OSGi中的服务需要两步。第一,从服务注册表中检索间接的服务引用。第二,使用这个间接引用去访问服务对象的实例。服务的引用能够被安全地赋值给成员变量,但是持有一个服务对象实例的引用通常不是一个好方法,因为服务可能动态地注销,这将导致持有服务的过时引用阻止垃圾回收器回收卸载的bundle。
服务的实现和客户端都应该打包进不同的bundle
JAR文件中。每个bundle的元数据声明了相应的激活器,但是服务实现是导出org.foo.hello包,而客户端则将其导入。注意,客户端bundle的元数据仅需要声明将Greeting接口包导入即可,它没有直接依赖服务的实现。这就使在不重启客户端bundle的情况下,动态的交换服务实现包变得更容易。欲看该实例的运行效果,请到本书配套源码中的chapter01greeting-exampleservice目录下,输入ant进行构建,并使用java
–jar main.jar命令来运行该实例
我们已经看了一些例子,相信你已经更好地理解了OSGi的各层是如何搭建在前一层基础上的。构建应用程序时,OSGi的每一层都能提供一些额外的功能,因为OSGi技术足够灵活,使你能够根据具体的需求来加入这些功能。如果在你的项目中仅仅想要更好的模块化,那么使用模块层。如果想要初始化模块以及与模块层交互,那么使用模块层和生命周期层。如果想使用动态的、面向接口的开发方式,那么使用所有三层。一切由你自己选择。
1.3.4 场景设置
在接下来的三章中,我们将使用一个简单的绘图程序,来帮助介绍OSGi框架中每一层的概念,绘图程序的用户界面如图1-7所示。介绍该绘图程序的目的并不是要介绍它的功能,而是要演示一些常见问题和最佳实践。
图1-7 简单绘图程序的用户界面
从功能的角度来看,该绘图程序仅能允许用户绘制不同的图形,例如圆形、正方形、三角形。这些图形会被填充预定义的颜色。可绘制的图形以按钮的形式显示在主窗口的工具栏上。为了绘制图形,用户需先从工具栏中选择相应的图形,然后在画布的任意地方点击即可。在画布中点击多次重复绘制同样的图形。用户可以通过拖动将图形移动到新的位置。这听起来非常简单。在介绍执行时的动态性时,通过一个图形程序来演示这些概念的真正价值就会显现出来。
我们已经大致介绍完了OSGi框架,在准备探究细节之前,将通过讨论与OSGi相似和相关的技术来了解它的来龙去脉。尽管没有Java技术与OSGi完全吻合,但技术总有相通相似处,在深入理解OSGi之前了解它们之间的关联性是很有必要的。
1.4 OSGi的相关技术
提及OSGi时通常也会提到很多其他技术,但在Java世界中OSGi具有其非常独特的位置。近些年来,没有哪项技术能解决OSGi所能解决的所有问题,但会有重叠、补充或者分支。尽管不大可能介绍OSGi与每项可想到的技术有何关联,但我们仍然会大致按照时间顺序介绍一些与OSGi紧密相关的技术。通过阅读本章内容,你应该能够有一个清晰的判断:OSGi是否可以取代你所熟悉的一些技术,或者是对它们的补充?
1.4.1 Java EE
Java Enterprise Edition(Java EE,也就是之前的J2EE)的历史可以追溯到1997年。最初,Java
EE和OSGi分别面向计算领域的两极(前者面向企业应用市场而后者面向嵌入式应用市场)。直到过去几年,OSGi技术才真正开始扎根于企业应用领域。
总的来看,Java EE API栈与OSGi并没有直接的关系。在Java
EE领域,企业JavaBeans(EJB)规范也许和OSGi最为接近,EJB定义了一个组件模型和包的组织形式。但是EJB中的组件模型侧重于为企业应用提供一种标准的实现方式,这些企业应用必须定期处理持久化、事务及安全等问题。EJB在部署描述及包的组织形式方面相对简单,对组件的完整生命周期也没有表述,同时也不支持清晰明确的模块化概念。
OSGi已经进入Java EE领域,OSGi作为Java
EE现有技术的基础为其提供了一个非常完备的模块层。因为这两个领域相互忽略了很久,将已经存在的Java
EE概念向OSGi转移时将面临一些挑战,主要是两者采用了不同的类加载机制。但是,目前仍然取得了很大进展。现在OSGi在所有主流应用服务器中都扮演了重要角色,例如IBM的WebSphere,Red
Hat的JBoss,Oracle的GlassFish,ObjectWeb的JOnAS和Apache的Geronimo。
1.4.2 Jini
Jini是一项经常被忽视的Java技术,在概念上Jini绝对称得上是OSGi的同胞兄弟。Jini要解决的问题与OSGi一样,都植根于拥有大量互联设备的网络环境。
Sun对Jini的研发始于1998年,目标是通过一种易扩展的动态服务组的方式来管理网络互联环境。Jini引入了服务提供者、服务消费者以及服务查找注册的概念。所有这些听起来都与OSGi相似,而区别在于Jini面向分布式系统。在典型的Jini场景中,消费者使用一种远程过程调用机制(类似RMI),通过某种形式的代理连接到客户端。服务查找注册本身也是一个可以远程访问的联合服务。Jini设定的场景是多个VM进程之间实现远程访问,而OSGi设定的场景是所有的一切都发生在单个VM进程中。然而与OSGi形成鲜明对比的是,Jini没有定义任何模块化机制,并且依赖于RMI的执行时代码加载特性。开源项目Newton是一个例子,它把OSGi和Jini整合到一个框架之中。
1.4.3 NetBeans
作为面向Java的IDE及应用程序平台,NetBeans在模块化设计方面有着悠久的历史。Sun公司于1999年收购NetBeans并且一直对其进行改进。
NetBeans平台与OSGi有很多的相似之处。NetBeans定义了一个非常成熟的模块层,同时也提倡基于接口的编程方式,并且使用了查找模式,该模式类似OSGi中的服务注册。由于OSGi专注于嵌入式设备和动态化机制,而NetBeans平台起初仅是服务于IDE的一个上层实现。最终该平台演化为一个独立的工具,但是NetBeans专注于成为一个完整的GUI应用平台,这包括对文件系统、窗口系统以及其他更多系统的抽象。NetBeans和OSGi很难相提并论,即便二者如今已不相上下,这或许是因为OSGi更加专注。
1.4.4 JMX
JMX(Java Management Extension)由JCP(Java Community
Process)于2000年作为JSR003发布。早期JMX被认为是可以和OSGi相提并论的技术。JMX这门技术用于管理和监控远程的应用程序、系统对象和设备。基于该目的,JMX定义了一个服务器和一个组件模型。
但在真正意义上JMX还不能和OSGi相比拟,它仅仅是OSGi的一个补充,因为JMX可用来管理和监控OSGi框架以及其所包含的bundles和服务。为何在一开始将JMX与OSGi放在一起进行比较?大概有三个原因:JMX组件模型非常通用,所以一般用它来构建应用程序。JMX规范中定义了一套机制用于将动态代码加载到服务器中;某些早期的使用者推动JMX朝这个方向发展(其中JBoss是一个主要的推动者)。通过采纳并扩展JMX,使其成为JBoss应用服务器中的模块层。(从JBoss
5开始取消了JMX),如今对于JMX来说,它不会(也不应该)再和一个模块化系统相混淆了。
1.4.5 轻量级容器
2003年左右,轻量级或者控制反转(IoC)容器开始出现,如Pico Container、Spring和Apache
Avalon。这一系列IoC容器背后的主要思想是,通过优先使用接口避免使用具体类型的方式简化组件的配置和组装。与Ioc容器联合使用的还有依赖注入技术,该技术使得组件依赖于接口类型,接口的实现被注入到组件的实例中。OSGi服务进一步改进了类似的基于接口的方式,但它采用了一种服务定位器(service-locator)模式,并以此来打破组件之间对具体实现的依赖,这与Apache
Avalon类似。
与此同时,Service
Binder项目为OSGi组件提供了依赖注入框架。由此不难理解为何会出现对二者的比较。无论如何,OSGi对基于接口的服务以及服务定位器模式的使用远超前于当前趋势,没有哪一项技术能够像OSGi那样提供一种完备的动态模块层。现在IoC厂商正经历着重大转变,将他们的基础架构向OSGi框架迁移,比如VMware(之前的SpringSource)在OSGi
Blueprint规范(将在第12章讨论)上所做的工作。
1.4.6 Java 业务集成
JBI (Java Business
Integration)是由JCP开发的,并于2005年发布,旨在构建标准的SOA平台。基于该平台可构建企业应用集成(EAI)和B2B(Business-to-Business)集成方案。
在JBI中,如果将插件组件集成到JBI框架里,那么这些插件组件就可以提供服务或者使用服务。与OSGi类似,这些组件并不直接与服务交互,取而代之的是使用标准的基于WSDL(Web
Services Description Language)Web服务描述语言的消息实现间接通信。
JBI使用基于JMX的方法管理组件的安装过程和生命周期,并为所有组件定义了包的封装结构。因为和OSGi架构具有内在的相似性,JBI很容易被误解为与OSGi的作用相同。事实恰恰相反,JBI具有极其简单的模块化机制,主要解决的是将基本组件集成到框架的问题。不难看出,对于JBI来说,使用OSGi更加成熟的模块化会更有意义,这也最终导致了Sun公司的Fuji项目和Apache的ServiceMix项目的出现。
1.4.7 JSR 277
2005年,Sun发布了为Java定义模块化系统的JSR 277 (“Java模块化系统”),希望通过JSR
277为Java平台定义一种模块化框架、包的封装结构、仓库系统等。从OSGi联盟的角度来说,这是一个重新发明轮子的经典案例,因为一切都是从头开始,构建工作并没有以从OSGi获得的经验为基础。
2006年,众多OSGi的支持者急切要求引入JSR
291(标题为“Java的动态组件支持”),这是为了将OSGi技术适当地引入JCP标准化;其目的有两个:建立两个社区之间联系的纽带,同时也确保JSR
277中考虑对OSGi技术的集成。JSR 291完成的速度非常快,主要是因为它基于OSGi
R4规范,并导致R4.1规范的发布。在这期间,OSGi技术继续保持上涨的势头。而直到2008年,JSR
277的进展仍然非常缓慢,直到被无限期地搁置。
1.4.8 JSR 294
2006年,JSR 294(标题为“改进的Java编程语言的模块化支持)是作为JSR 277的分支被引入的。作为JSR
294的重要目标,为了实现模块化,Java语言本身需要做出相应的改变。JSR
294的最初想法是将超级包(superpackage)的概念引入Java语言——一个包包含多个包。
超级包的规范陷入了细节的泥潭中,直到后来要将一个模块访问修饰符关键字添加到语言中而被拆解。简化的处理方式最终也导致了JSR
294被彻底抛弃,并在2007年被重新合并到JSR 277。2008年,当JSR 277被搁置的可能性越来越大后,JSR
294又被重新提出,用于声明一个模块级的访问修饰符。
由于JSR 277被搁置,Sun公司引入了一个内部项目,称为Project
Jigsaw,用于将JDK模块化。在Sun被Oracle被收购后,Jigsaw仍然在不断演进。
1.4.9 SCA
SCA(Service Component
Architecture)是于2004年成立的一个行业协作组织,并在2007年催生了最终的规范。SCA定义了一个与OSGi类似的面向服务的组件模型。在该模型中,组件提供并请求服务。该组件模型相对来说更加高级,因为它为完全递归组件模型定义了复合组件(composite
component)。复合组件是由其他组件组成的组件。
SCA旨在成为一个用于声明式组合组件的组件模型,在实现的过程中,使用了多种技术,如Java、 BPEL
(业务处理执行语言)、EJB和C++,另外还通过多种绑定方式实现了集成,如SOAPHTTP、 JMS (Java Message
Service)、JCA(Java EE Connector Architecture),以及IIOP(Internet
Inter-Orb
Protocol)。SCA明确定义了一个标准的包结构,但其并非像OSGi那样提供了一个复杂的组件层。SCA规范允许使用其他包结构,这样一来就可以在基于Java的SCA实现中使用OSGi进行包封装,并将其作为模块层。Apache
Tuscany和Newton是使用OSGi实现SCA的例子。除此以外,bundle可以被用于实现SCA组件的类型,而SCA也可作为一种机制使用,以提供对OSGi服务的远程访问。
1.4.10 .NET
虽然微软的.NET技术(发布于2002年)与Java并没有直接的关系,但还是值得一提。.NET在很大程度上受到了Java的启发,并在此基础上做过许多改进,这类似于OSGi对Java的改进。不仅仅是将Java作为榜样,微软也从处理DLL的痛苦历史中吸取了教训。最终的结果就是,.NET中使用了具有模块化性质的集合(assembly)概念,类似于OSGi中的bundle。所有的.NET代码都会被打包到集合中,并采用DLL或EXE文件的形式。集合提供了对其所包含代码的封装机制;internal访问修饰符表明代码的可见性局限于集合内部,而非外部可见。集合还包含了元数据,用于描述对其他集合的依赖关系,但整个模型的灵活性远不及OSGi。因为集合之间的依赖性取决于具体的集合版本,所以OSGi中提供者的可替换性是无法实现的。
在执行的过程中,集合被载入应用域,并只能在卸载整个应用域时被卸载。这就导致难以实现OSGi的高度动态和轻量级特性。因为当多个集合被载入到同一个应用域后,只能在同一时刻被卸载。虽然将集合载入到不同的域中也是可以的,但是跨域的通信必须通过进程间通信协作实现,与此同时,集合之间的类型共享也是非常复杂的。目前已开展了一些研究工作,目的是为.NET平台构建类似于OSGi的环境,但.NET和Java平台生来不同,这就导致二者并没有太多共性。尽管如此,在该领域中,.NET对于标准Java的促进作用还是有目共睹的。
1.5 小结
本章是本书其他内容的基础,介绍了如下内容。
Java平台是非常好的应用开发平台,但它对模块化的支持很大程度上受限于细粒度的面向对象机制而不是粗粒度的模块化特征,而后者却是工程管理中需要的。
为了弥补Java在模块化上的不足,OSGi服务平台通过OSGi框架创建了一种强大灵活的解决方案。
OSGi采用声明、基于元数据的方式,充分利用了OSGi复杂的模块化功能,提供了一种非侵入的使用方法,这种方法不修改或少修改代码就可以完成对项目的封装。
OSGi框架为了简化管理,定义了一套可控的动态模块化生命周期。
OSGi遵循良好的设计原则,提供了基于接口的编程方式,从而可以分离接口与实现。
在对Java的局限性和OSGi能力有了高层次的认识后,第2章通过深入分析模块层的细节开始我们的技术探险之旅。这是OSGi世界中其他一切的根基。