探索 OSGi 框架的组件运行机制
在目前的 Java 开发平台中,对于组件开发过程,比如打包、部署和验证等,并没有一个统一的标准。正因如此,许多 Java 项目,例如 JBoss 和 Net Beans,都拥有一套自定义的组件开发规范和框架,但是实际上这些解决方案都是基于为组件分配独立的类加载器 (Class Loader) 的思想。 OSGi 框架为基于 Java 的组件开发提供了一套通用的和标准的解决方案,并已经成为事实上的工业标准。
OSGi 组件框架
在 OSGi 框架中,组件被称为 Bundle,它是由一些提供 Bundle 自身功能的资源 ( 如 Java 类文件、配置文件等 )、MANIFEST.MF 文件和其它资源文件构成。在运行时环境中,每个 Bundle 都有一个独立的 Class Loader,Bundle 之间通过不同的类加载器和在 MANIFEST.MF 文件中定义的包约束条件就可以轻松实现包级别的资源隐藏和共享,从而实现基于组件方式的开发和运行。 Class Loader 是实现这种运行方式的关键机制,每个 Bundle 的 Class Loader 必须在此 Bundle 得到正确的解析 ( Resolving ) 之后,再由 OSGi 框架创建。只有当一个 Bundle 中的所有包约束条件都满足时,它才被正确解析完毕。
Bundle 解析
Bundle 的解析是通过分析定义在 MANIFEST.MF 文件中的元数据 ( 主要定义了包约束条件 ),查找与包约束条件相匹配的 Bundle 并建立关联关系的过程。 MANIFEST.MF 文件中的包约束条件主要是通过 Import-Package、DynamicImport-Package、Export-Package 和 Require-Bundle 这四种表达方式来实现。下面简单介绍一下它们:
- Import-Package:定义需要导入的包。默认是所有需导入的包必须都能够找到相应的导出 Bundle (Exporter),否则解析失败。
- Export-Package:定义导出的包。在一个 Bundle 里面,一个包的导出定义并不意味着相应的包导入定义,而是这些类资源会在 Bundle 自身的类路径里面查找和加载。
- Require-Bundle:定义依赖的 Bundle 。
- DynamicImport-Package:定义需要动态导入的包。这部分定义没有在 Bundle 解析过程中使用,而是在运行时动态解析并加载共享包。
在 Bundle 得到正确解析后,OSGi 框架将会生成此 Bundle 的依赖关系表。在实际运行过程中,框架就可以通过此关系表找到 Bundle 依赖的外部 Class Loader,从而实现外部类资源的加载和运行 hg 。 Bundle 的关系图可以在 OSGi 的控制台中通过内部命令" bundle "来查看,如下图所示:
图 1. Bundle 依赖关系表
Bundle 运行
Bundle 的运行主要依靠于 OSGi 框架为其创建的类加载器(Class Loader),加载器负责查找和加载 Bundle 自身或所依赖的类资源。 ClassLoader 之间的依赖关系构成了一个有向图,如下图所示:
图 2. Class Loader 依赖关系图
Bundle 的 Class Loader 能加载的所有类的集合构成了 Bundle 的类空间 (Class Space) 。类空间包含的类资源主要来自于以下几个方面:
- 父 Class Loader 可加载的类集合;
- Import-Package 定义的依赖的包;
- Require-Bundle 定义的依赖的 Bundle 的类集合;
- Bundle 自身的类集合,通常在 Bundle-Classpath 中定义;
- 隶属于 Bundle 的 Fragment 类集合。
在实际运行环境中,Bundle 的 Class Loader 根据如下规则去搜索类资源。规则简要介绍如下:
- 如类资源属于 java.* 包,则将加载请求委托给父加载器;
- 如类资源定义在 OSGi 框架中启动委托列表(org.osgi.framework.bootdelegation)中,则将加载请求委托给父加载器;
- 如类资源属于在 Import-Package 中定义的包,则框架通过 Class Loader 依赖关系图找到导出此包的 Bundle 的 Class Loader,并将加载请求委托给此 Class Loader ;
- 如类资源属于在 Require-Bundle 中定义的 Bundle,则框架通过 Class Loader 依赖关系图找到此 Bundle 的 Class Loader,将加载请求委托给此 Class Loader ;
- Bundle 搜索自己的类资源 ( 包括 Bundle-Classpath 里面定义的类路径和属于 Bundle 的 Fragment 的类资源);
- 若类在 DynamicImport-Package 中定义,则开始尝试在运行环境中寻找符合条件的 Bundle 。
如果在经过上面一系列步骤后,仍然没有正确地加载到类资源,则 OSGi 框架会向外抛出类未 发现异常。
确保 Bundle 类空间的完整性
从上述对 OSGi 组件框架的剖析中,MANIFEST.MF 文件中定义的元数据对 Bundle 的解析和运行起着至关重要的作用。 Bundle 所需要的类资源应该完全被其类空间所覆盖,否则将会在运行时环境中抛出类或资源未发现异常,从而导致 Bundle 无法正常工作。下面给出一个实际例子来说明这种情况。
在 Eclipse 环境中创建两个插件开发项目 , 并命名为 Client 和 HelloService. HelloService 向外提供服务 , Client 通过注册在 OSGi 控制台中的命令来调用 HelloService 的服务。 HelloSerive 定义的服务接口为 :
清单 1. IHello.java
1 2 3 4 |
|
清单 2. ILabelProvider.java
1 2 3 4 5 |
|
同时 HelloService 还给出了具体实现 :
清单 3. HelloImpl.java 和 Resource.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
由于此 Bundle 向外提供了服务和资源信息,因此需要在 MANIFEST.MF 文件中将它们所属于的包导出:
清单 4. MANIFEST.MF
1 2 3 4 5 6 7 8 9 10 |
|
在 BundleActivator 类中,向 OSGi 框架注册了 IHello 的 service,具体实现代码十分简单,在此略过。服务使用方 (Client Bundle),是一个控制台程序,通过注册在 OSGi 框架中的命令调用 HelloService Bundle 提供的服务。具体实现参见如下代码:
清单 5. Activator.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
|
在以上代码中,Bundle 通过 ServiceTracker 得到 IHello 这个服务接口,并同时向 OSGi 框架注册一个名为" call "的命令。在这个命令的执行过程中,Bundle 通过自身的 ClassLoader 去加载定义在 LabelProvider.properties 文件中的 ILabelProvider 实现类,并打印 Label Provider 中的内容。从以上代码中,我们会很容易地定义出此 Bundle 需要导入哪些包,如下 MANIFEST.MF 文件所示 :
代码清单 6: MANIFEST.MF
1 2 3 4 5 6 7 8 9 10 11 12 |
|
从表面上看 , 这个文件没有任何遗漏 , 在 Eclipse IDE 环境中能够正确解释通过 , 并且在启动 OSGi 框架后 , Client Bundle 也能够被正确解析和启动。但是在敲入 call 命令的后 , 就会显示如下错误 :
图 3. 资源加载异常
原来缺少了一个依赖包 com.ibm.helloservice.resource.Resource。在 MANIFEST.MF 文件中加入对它的定义后 , call 命令就能够正确运行了 :
图 4. 运行结果
这个例子模拟了在实际 Bundle 开发过程中普遍遇到的问题,即许多类资源定义在 plugin.xml 文件中,然后由其他组件动态的创建和调用,而在 Eclipse IDE 开发环境中,并没有相应的机制去检查非 Java 代码里的包引用,从而 Bundle 缺失对某些包依赖的定义,最后就造成了 Bundle 类空间的不完整性。通常这种情况下,Bundle 能够被正确解析,但是在运行的时候就会抛出类未找到异常。所以在开发一个 Bundle 的时候,一定要仔细检查类空间是否已经将整个 Bundle 所需要的类资源覆盖,这样才能避免运行时的异常发生。
合理使用 Manifest 文件
Import-Package 与 Require-Bundle
OSGi 框架提供了两种导入包依赖的方式,即 Import-Package 和 Require-Bundle 。从下图中我们可以看出 Require-Bundle 会对整个 Bundle 产生依赖,也就是说 Bundle 所 Export 出的包都会被 A 加入到自己的类空间,而 Import-Package 只会对指定的包产生依赖关系。
图 5. Bundle 依赖关系图
在大多数情况下,都应该使用 Import-Package 而不是 Require-Bundle 。 Import-Package 比 Require-Bundle 更利于 Bundle 的部署和版本维护,同时在查找类的时候有更高的效率。
Eclipse-LazyStart
在 OSGi R4 版本中,通过对 Eclipse-LazyStart 属性的设置,可以指定 Bundle 是否支持慢启动功能。当 Eclipse-LazyStart 被设置为 true 的时候,Bundle 不会被默认启动,而是当 Bundle 的类或其它资源被第一次调用时,由框架自动激活 Bundle 。这样就会使得 OSGi 框架在启动的时候,只启动必须的 Bundle 而延迟启动其它的 Bundle,从而确保了启动时间和效率。在默认情况下,Eclipse-LazyStart 为 false 。
Bundle-ManifestVersion
Bundle-ManifestVersion 指定了 Bundle 应该遵循 OSGi 规范的版本号。其默认值是 1,即 R3 版本;值为 2 的时候,表示为 R4 版本。当 Bundle 需要用到 R4 中新功能的时候,如 Bundle 的慢启动,则必须显示设置 Bundle-ManifestVersion 为 2 。
类不一致性问题
在 OSGi 框架中,多个 Bundle 可以导出相同的包,如果在某个 Bundle 的类空间中存在来自于不同 Bundle 的相同类信息,就会导致类的不一致性问题。示例如下:
图 6. Bundle 依赖关系图
Bundle A 向外 Export 两个包 p 和 r,其中 p 对 q 存在约束关系,即 q 须为 Bundle B 中的 1.0 版本。同时,Bundle C 又分别导入了 p 和 q 两个包,包 p 来自于 A,而包 q 为 Bundle D 中的 2.0 版本。虽然 C 中并没有显示地定义对 B 中包 q 的依赖关系,但是由于 A 中的包 p 绑定了 B 中版本为 1.0 的包 q,故 C 在解析对包 p 的依赖关系的时候也会自动把 B 中 1.0 版本的包 q 导入到自己的类空间中。这样在 C 的类空间中,就存在着两个不同版本、来自于不同 Bundle 的包 q,进而就会存在两个不同的 Class Loader 对应着包 q,故在 Bundle C 的运行过程中就会出现类不一致性的异常。
小结
本文介绍了 OSGi 框架的组件运行机制,包括 Bundle 的解析、运行等,并结合实际的示例演示了在基于 OSGi 平台开发 Bundle 的过程中应该注意的一些问题。
相关主题
- 获得有关 Eclipse 和 Equinox 的更多详细资料:Eclipse.org,Equinox。
- 阅读“类装入问题解密”(developerWorks,2005 年 12 月):了解 Java Class Loader 的相关知识:。
- 阅读 Scott Delap 撰写的“了解 Eclipse 插件如何使用 OSGi”(developerWorks,Scott Delap,2006 年 9 月):阐明了 Eclipse 与 OSGi 的关系。
- 有关 Eclipse 和 OSGi 的简介,请阅读“利用 OSGi 解决 Eclipse 插件难题”。
- 访问 OSGi Alliance Service Platform, 了解更多关于 OSGi 的信息,包括 OSGi Release 4 规范等信息。
- 要获得在 Eclipse 下进行开发的详细帮助文档,请访问 Help – Eclipse SDK。
- 要获得关于 Eclipse 平台的介绍性文章,请参阅“Eclipse 平台入门”。
- 访问 IBM developerWorks Eclipse 项目资源,扩展您的 Eclipse 技术。
- developerWorks Java 技术专区:数百篇关于 Java 编程各个方面的文章。
from:https://www.ibm.com/developerworks/cn/java/j-lo-osgi/index.html
探索 OSGi 框架的组件运行机制相关推荐
- OSGi 框架的组件运行机制
在目前的 Java 开发平台中,对于组件开发过程,比如打包.部署和验证等,并没有一个统一的标准.正因如此,许多 Java 项目,例如 JBoss 和 Net Beans,都拥有一套自定义的组件开发规范 ...
- php的前端组件化,简要剖析PHP的Yii框架的组件化机制的基本知识
组件是 Yii 应用的主要基石.是 yii\base\Component 类或其子类的实例.三个用以区分它和其它类的主要功能有: 属性(Property) 事件(Event) 行为(Behavior) ...
- OSGi 规范和框架 OSGi框架类加载机制 Java默认类加载器机制和OSGI类加载器机制比较
一.OSGi 规范 OSGi(Open Service Gateway Initiative) 技术是 Java 动态化模块化系统的一系列规范.OSGi 一方面指维护 OSGi 规范的 OSGi Al ...
- 日志采集框架Flume、Flume介绍、概述、运行机制、Flume采集系统结构图(1、简单结构、复杂结构)
1. 日志采集框架Flume 1.1 Flume介绍 1.1.1 概述 1.Flume是一个分布式.可靠.和高可用的海量日志采集.聚合和传输的系统. 2.Flume可以采集文件,socket数据包等各 ...
- 日志采集框架Flume、Flume介绍、概述、运行机制、Flume采集系统结构图(1、简单结构、复杂结构)...
1. 日志采集框架Flume 1.1 Flume介绍 1.1.1 概述 1.Flume是一个分布式.可靠.和高可用的海量日志采集.聚合和传输的系统. 2.Flume可以采集文件,socket数据包等各 ...
- COM组件的运行机制
COM组件的运行机制 构造一个创建COM组件的最小框架结构 IUnknown *pUnk=NULL; IObject *pObject=NULL; CoInitialize(NULL); CoCrea ...
- 实时计算框架:Flink集群搭建与运行机制
一.Flink概述 1.基础简介 Flink是一个框架和分布式处理引擎,用于对无界和有界数据流进行有状态计算.Flink被设计在所有常见的集群环境中运行,以内存执行速度和任意规模来执行计算.主要特性包 ...
- [转载]深入探索.NET框架内部了解CLR如何创建运行时对象
深入探索.NET框架内部了解CLR如何创建运行时对象 发布日期: 9/19/2005 | 更新日期: 9/19/2005 Hanu Kommalapati Tom Christian 本文讨论: • ...
- 天龙源码框架分析_MySQL8-InnoDB总体架构和运行机制的系统分析(上)
1. 前文回顾:四个阶段和两种方法 首先让我们回顾下,在上一篇文章介绍的MySQL8代码分析的四个阶段和两种方法. 四个阶段: 借鉴瀑布式软件开发流程,我们将从熟悉MySQL的使用和运维,到吃透MyS ...
最新文章
- numpy.random.randn()
- nodejs开发部署工具,守护进程
- SAP CRM WebClient UI搜索结果界面的扩展字段显示
- linux更新命令yum,Linux中升级更新命令yum upgrade和yum update的区别
- 比较全的C语言面试题
- 神盾解密工具 之 解密 “ PHP 神盾解密工具 ”
- 微信小程序实现转义换行符
- 关于SQL Server 2005 的自动远程数据库备份
- MySQL引擎类型(三)
- 条件指示符 #ifdef 的用法
- opensuse安装pycharm
- JDK下载与安装教程
- Aspenone.hysys.V7.1多国语言包(含中文)
- 测试raid性能软件,IO性能测试-关于RAID不可不知的那些事儿
- 【Redis】模拟手机验证码案例:Jedis使用、get、hget、hincrBy、setex、生成6位随机数工具
- android 国际电话区号,中国国际区号_电话区号_中国区号是多少-中国区号查询
- mysql从指定条数开始查询_mysql数据库指定条数数据查询的主要方法
- celeste第二章_『第二章』 蔚蓝的剑
- 在线的色值转换RGB网址
- 简单的LED交替闪烁程序
热门文章
- Redis双机热备方案--转
- 在程序中进行make以后出现的一些错误以及解决方法
- Yongkil Kwon:EOS具有当今世界上最多中心化的协议 | 独家专访
- 解密个性化资讯推荐技术
- 天通苑海鲜餐馆数据调查,很难想象消费越贵越受欢迎
- Algorithms_算法思想_递归分治
- Elasticsearch-03 CentOS7 / Windows上部署Elasticsearch5.6.16集群模式
- Redis-10Redis的事务回滚
- Spring MVC-06循序渐进之Converter和Formatter
- Oracle-分析函数之取上下行数据lag()和lead()