OSGI 理论知识
下面列出了主要的控制台命令:
表 1. Equinox OSGi 主要的控制台命令表
类别 | 命令 | 含义 |
控制框架 | launch |
启动框架 |
shutdown |
停止框架 | |
close |
关闭、退出框架 | |
exit |
立即退出,相当于 System.exit | |
init |
卸载所有 bundle(前提是已经 shutdown) | |
setprop |
设置属性,在运行时进行 | |
控制 bundle | Install |
安装 |
uninstall |
卸载 | |
Start |
启动 | |
Stop |
停止 | |
Refresh |
刷新 | |
Update |
更新 | |
展示状态 | Status |
展示安装的 bundle 和注册的服务 |
Ss |
展示所有 bundle 的简单状态 | |
Services |
展示注册服务的详细信息 | |
Packages |
展示导入、导出包的状态 | |
Bundles |
展示所有已经安装的 bundles 的状态 | |
Headers |
展示 bundles 的头信息,即 MANIFEST.MF 中的内容 | |
Log |
展示 LOG 入口信息 | |
其它 | Exec |
在另外一个进程中执行一个命令(阻塞状态) |
Fork |
和 EXEC 不同的是不会引起阻塞 | |
Gc |
促使垃圾回收 | |
Getprop |
得到属性,或者某个属性 | |
控制启动级别 | Sl |
得到某个 bundle 或者整个框架的 start level 信息 |
Setfwsl |
设置框架的 start level | |
Setbsl |
设置 bundle 的 start level | |
setibsl |
设置初始化 bundle 的 start level |
MANIFEST.MF
MANIFEST.MF 可能出现在任何包括主类信息的 Jar 包中,一般位于 META-INF 目录中,所以此文件并不是一个 OSGi 特有的东西,而仅仅是增加了一些属性,这样也正好保持了 OSGi 环境和普通 Java 环境的一致性,便于在老的系统中部署。表 2 列出此文件中的重要属性及其含义:
表 2. MANIFEST.MF 文件属性
属性名字 | 含义 |
---|---|
Bundle-Activator |
Bundle 的启动器 |
Bundle-SymbolicName |
名称,一般使用类似于 JAVA 包路径的名字命名 |
Bundle-Version |
版本,注意不同版本的同名 bundle 可以同时上线部署 |
Export-Package |
导出的 package 声明,其它的 bundle 可以直接引用 |
Import-Package |
导入的 package |
Eclipse-LazyStart |
是否只有当被引用了才启动 |
Require-Bundle |
全依赖的 bundle,不推荐 |
Bundle-ClassPath |
本 bundle 的 class path,可以包含其它一些资源路径 |
Bundle-RequiredExecutionEnvironment |
本 bundle 必须的执行环境,例如 jdk 版本声明 |
重要的理论知识
好的,刚才我们已经从头到尾开发了一个基于 Equinox 框架的 Hello world 应用程序。我们发现似乎并不是很困难,很多工作 Eclipse 已经帮我们做好了,例如 Activator 代码框架和 MANIFEST.MF 文件,我们也学会了如何控制 OSGi 的控制台和编写 MANIFEST.MF 文件,但是,您真的明白它们是如何运行的么?下面我们将重点介绍一些 OSGi 运行必备的基础知识。
什么是 bundle?
我们已经看到,编写一个很普通的 Hello world 应用,必须首先创建一个 plug-in 工程,然后编辑其 Activator 类的 start
方法,实际我们这样做的本质是为 OSGi 运行环境添加了一个 bundle,那么一个 bundle 必须的构成元素是哪些呢?
- MANIFEST.MF:描述了 bundle 的所有特征,包括名字、输出的类或者包,导入的类或者包,版本号等等,具体可以参考 表 2. MANIFEST.MF 文件属性。
- 代码:包括 Activator 类和其它一些接口以及实现,这个和普通的 Java 应用程序没有什么特殊的区别。
- 资源:当然,一个应用程序不可能没有资源文件,比如图片、properties 文件、XML 文件等等,这些资源可以随 bundle 一起存在,也可以以 fragment bundle 的方式加入。
- 启动级别的定义:可以在启动前使用命令行参数指定,也可以在运行中指定,具体的 start level 的解释,请参考 后面的说明。
框架做了些什么?
好了,我们已经明白 bundle 是什么了,也知道如何开发一个基本的 bundle 了,那么我们还必须要明白,我的 bundle 放在 Equinox 框架中,它对我们的 bundle 做了些什么?
图 11. Equinox 框架架构
实际上,目标平台已经为我们准备了 N 个 bundle,它们提供各种各样的服务,OSGi 中,这些 bundle 的名字叫 system bundle,就好比精装修的房子,您只需要拎包入住,不再需要自己铺地板,装吊顶了。
我们的 bundle 进入 Equinox 环境后,OSGi 框架对其做的事情如下:
- 读入 bundle 的 headers 信息,即 MANIFEST.MF 文件;
- 装载相关的类和资源;
- 解析依赖的包;
- 调用其 Activator 的
start
方法,启动它; - 为其提供框架事件、服务事件等服务;
- 调用其 Activator 的
stop
方法,停止它;
Bundle 的状态变更
OK, 现在我们大概明白了一个 bundle 的定义和其在 OSGi 框架中的生命周期,前面我们看到控制台可以通过 ss
命令查看所有装载的 bundle 的状态,那么 bundle 到底具有哪些状态,这些状态之间是如何变换呢?我们知道了这些状态信息,对我们有何益处?
首先,了解一下一个 bundle 到底有哪些状态:
表 3. Bundle 状态表
状态名字 | 含义 |
---|---|
INSTALLED |
就是字面意思,表示这个 bundle 已经被成功的安装了 |
RESOLVED |
很常见的一个状态,表示这个 bundle 已经成功的被解析(即所有依赖的类、资源都找到了),通常出现在启动前或者停止后 |
STARTING |
字面意思,正在启动,但是还没有返回,所以您的 Activator 不要搞的太复杂 |
ACTIVE |
活动的,这是我们最希望看到的状态,通常表示这个 bundle 已经启动成功,但是不意味着您的 bundle 提供的服务也是 OK 的 |
STOPPING |
字面意思,正在停止,还没有返回 |
UNINSTALLED |
卸载了,状态不能再发生变更了 |
下面请看一张经典的 OSGi bundle 变更状态的图:
图 12. OSGi bundle 变更状态图
Bundle 导入导出 package
OK,到现在为止,似乎一切都是新鲜的,但是您似乎在考虑,OSGi 到底有什么优势,下面介绍一下其中的一个特点,几乎所有的面向组件的框架都需要这一点来实现其目的:面向服务、封装实现。这一点在普通的 Java 应用是很难做到的,所有的类都暴露在 classpath 中,人们可以随意的查看您的实现,甚至变更您的实现。这一点,对于希望发布组件的公司来说是致命的。
图 13. OSGi bundle 原理
OSGi 很好的解决了这个问题,就像上面的图显示的,每个 bundle 都可以有自己公共的部分和隐藏的部分,每个 bundle 也只能看见自己的公共部分、隐藏部分和其它 bundle 的公共部分。
bundle 的 MANIFEST.MF 文件提供了 EXPORT/IMPORT package 的关键字,这样您可以仅仅 export 出您希望别人看到的包,而隐藏实现的包。并且您可以为它们编上版本号,这样可以同时发布不同版本的包。
Bundle class path
这一点比较难理解,一般情况下您不需要关心这个事情,除非事情出现了问题,您发现明明这个类就在这里,怎么就是报告 ClassNotFoundException/NoClassDefExcpetion 呢?在您垂头丧气、准备砸掉电脑显示器之前,请看一下 bundle 中的类是如何查找的:
- 首先,它会找 JRE,这个很明显,这个实际是通过系统环境的
JAVA_HOME
中找到的,路径一般是 JAVA_HOME/lib/rt.jar、tools.jar 和 ext 目录,endorsed 目录。 - 其次,它会找 system bundle 导出的包。
- 然后,它会找您的 import 的包,这个实际包含两种:一种是直接通过 require-bundle 的方式全部导入的,还有一种就是前面讲的通过 import package 方式导入的包。
- 查找它的 fragment bundle,如果有的话。
- 如果还没有找到,则会找自己的 classpath 路径(每个 bundle 都有自己的类路径)。
- 最后它会尝试根据 DynamicImport-Package 属性查找的引用。
启动级别 Start level
在 Equinox 环境中,我们在配置 hello world 应用的时候,看到我们将 framework start level 保持为 4,将 Hello world bundle 的 start level 设置为 5 。 start level 越大,表示启动的顺序越靠后。在实际的应用环境中,我们的 bundle 互相有一定的依赖关系,所以在启动的顺序上要有所区别,好比盖楼,要从打地基开始。
实际上,OSGi 框架最初的 start level 是 0,启动顺序如下:
- 将启动级别加一,如果发现有匹配的 bundle(即 bundle 的启动级别和目前的启动级别相等),则启动这个 bundle;
- 继续第一步,直到发现已经启动了所有的 bundle,且活动启动级别和最后的启动的 bundle 启动级别相同。
停止顺序,也是首先将系统的 start level 设置为 0:
- 由于系统当前活动启动级别大于请求的 start level,所以系统首先停止等于当前活动启动级别的 bundle;
- 将活动启动级别减一,继续第一步,直到发现活动启动级别和请求级别相等,都是 0。
开发一个真实的 OSGi 应用程序
我们不能只停留在 hello world 的层面,虽然那曾经对我们很重要 ,但是现实需要我们能够使用 OSGi 写出激动人心的应用程序,它能够被客户接受,被架构师认可,被程序员肯定。好的,那我们开始吧。下面将会着重介绍一些现实的应用程序可能需要的一些 OSGi 应用场景。
发布和使用服务
由于 OSGi 框架能够方便的隐藏实现类,所以对外提供接口是很自然的事情,OSGi 框架提供了服务的注册和查询功能。好的,那么我们实际操作一下,就在 Hello world 工程的基础上进行。
我们需要进行下列的步骤:
- 定义一个服务接口,并且 export 出去供其它 bundle 使用;
- 定义一个缺省的服务实现,并且隐藏它的实现;
- Bundle 启动后,需要将服务注册到 Equinox 框架;
- 从框架查询这个服务,并且测试可用性。
好的,为了达到上述要求,我们实际操作如下:
- 定义一个新的包
osgi.test.helloworld.service
,用来存放接口。单独一个 package 的好处是,您可以仅仅 export 这个 package 给其它 bundle 而隐藏所有的实现类 - 在上述的包中新建接口
IHello
,提供一个简单的字符串服务,代码如下:
清单 2. IHello
package osgi.test.helloworld.service; public interface IHello {
/**
* 得到 hello 信息的接口 .
* @return the hello string.
*/
String getHello();
} - 再新建一个新的包
osgi.test.helloworld.impl
,用来存放实现类。 - 在上述包中新建
DefaultHelloServiceImpl
类,实现上述接口:
清单 3. IHello 接口实现
public class DefaultHelloServiceImpl implements IHello { @Override
public String getHello() {
return "Hello osgi,service";
} } - 注册服务,OSGi 框架提供了两种注册方式,都是通过
BundleContext
类实现的:registerService(String,Object,Dictionary)
注册服务对象object
到接口名String
下,可以携带一个属性字典Dictionary
;registerService(String[],Object,Dictionary)
注册服务对象object
到接口名数组String[]
下,可以携带一个属性字典Dictionary
,即一个服务对象可以按照多个接口名字注册,因为类可以实现多个接口;
我们使用第一种注册方式,修改
Activator
类的start
方法,加入注册代码:清单 4. 加入注册代码
public void start(BundleContext context) throws Exception { System.out.println("hello world");
context.registerService(
IHello.class.getName(),
new DefaultHelloServiceImpl(),
null); } - 为了让我们的服务能够被其它 bundle 使用,必须在 MANIFEST.MF 中对其进行导出声明,双击 MANIFEST.MF,找到 runtime > exported packages > 点击 add,如图,选择 service 包即可:
图 14. 选择导出的服务包
- 另外新建一个类似于 hello world 的 bundle 叫:
osgi.test.helloworld2
,用于测试osgi.test.helloworld
bundle 提供的服务的可用性; - 添加 import package:在第二个 bundle 的 MANIFEST.MF 文件中,找到 dependencies > Imported packages > Add …,选择我们刚才 export 出去的 osgi.test.helloworld.service 包:
图 15. 选择刚才 export 出去的 osgi.test.helloworld.service 包
- 查询服务:同样,OSGi 框架提供了两种查询服务的引用
ServiceReference
的方法:getServiceReference(String)
:根据接口的名字得到服务的引用;getServiceReferences(String,String)
:根据接口名和另外一个过滤器名字对应的过滤器得到服务的引用;
- 这里我们使用第一种查询的方法,在
osgi.test.helloworld2
bundle 的Activator
的start
方法加入查询和测试语句:
清单 5. 加入查询和测试语句
public void start(BundleContext context) throws Exception {
System.out.println("hello world2"); /**
* Test hello service from bundle1.
*/
IHello hello1 =
(IHello) context.getService(
context.getServiceReference(IHello.class.getName()));
System.out.println(hello1.getHello());
} - 修改运行环境,因为我们增加了一个 bundle,所以说也需要在运行配置中加入对新的 bundle 的配置信息,如下图所示:
图 16. 加入对新的 bundle 的配置信息
- 执行,得到下列结果:
图 17. 执行结果
恭喜您,成功了!
OSGI 理论知识的更多相关文章
- js中函数的一些理论知识
函数的一些理论知识 1. 函数: 执行一个明确的动作并提供一个返回值的独立代码块.同时函数也是javascript中的一级公民(就是函数和其它变量一样). 2.函数的 ...
- 用VC进行COM编程所必须掌握的理论知识
一.为什么要用COM 软件工程发展到今天,从一开始的结构化编程,到面向对象编程,再到现在的COM编程,目标只有一个,就是希望软件能象积方块一样是累起来的,是组装起来的,而不是一点点编出来的.结构化编程 ...
- 图形学理论知识 BRDF 双向反射分布函数(Bidirectional Reflectance Distribution Function)
图形学理论知识 BRDF 双向反射分布函数 Bidirectional Reflectance Distribution Function BRDF理论 BRDF表示的是双向反射分布函数(Bidire ...
- TestNG学习-001-基础理论知识
此 文主要讲述用 TestNG 的基础理论知识,TestNG 的特定,编写测试过程三步骤,与 JUnit4+ 的差异,以此使亲对 TestNG 测试框架能够有一个简单的认知. 希望能对初学 TestN ...
- [转] DDD领域驱动设计(三) 之 理论知识收集汇总
最近一直在学习领域驱动设计(DDD)的理论知识,从网上搜集了一些个人认为比较有价值的东西,贴出来和大家分享一下: 我一直觉得不要盲目相信权威,比如不能一谈起领域驱动设计,就一定认为国外的那个Eric ...
- Winsock网络编程笔记(4)----基本的理论知识
前面的笔记记录了Winsock的入门编程,领略了Winsock编程的乐趣..但这并不能算是掌握了Winsock,加深理论知识的理解才会让后续学习更加得心应手..因此,这篇笔记将记录一些有关Winsoc ...
- Android初级教程对大量数据的做分页处理理论知识
有时候要加载的数据上千条时,页面加载数据就会很慢(数据加载也属于耗时操作).因此就要考虑分页甚至分批显示.先介绍一些分页的理论知识.对于具体用在哪里,会在后续博客中更新. 分页信息 1,一共多少条数据 ...
- Android初级教程理论知识(第四章内容提供器)
之前第三章理论知识写到过数据库.数据库是在程序内部自己访问自己.而内容提供器是访问别的程序数据的,即跨程序共享数据.对访问的数据也无非就是CRUD. 内容提供者 应用的数据库是不允许其他应用访问的 内 ...
- 关于mpi的理论知识以及编写程序来实现数据积分中的梯形积分法。
几乎所有人的第一个程序是从“hello,world”程序开始学习的 #include "mpi.h" #include <stdio.h> int main(int a ...
随机推荐
- 基于霸道秉火的STM32F103ZET6嵌入式开发之------基于定时TIM3的PWM实验
1:PWM脉冲宽度调制 STM32 的定时器除了 TIM6 和 7.其他的定时器都可以用来产生 PWM 输出.其中高级定时器 TIM1 和 TIM8 可以同时产生多达 7 路的 PWM 输出.而通用定 ...
- 高德地图API中折线polyline不能跨越180度经度线的解决方案
1.问题 最近在使用高德地图的API,有一个需求是画出对象的历史轨迹,采用了高德地图API中的折线polyline函数.但如果需要跨180度经度线的折线,会出现不能跨越的情况,如下图所示: 图中有三个 ...
- PTA7-2 愿天下有情人都是失散多年的兄妹
呵呵.大家都知道五服以内不得通婚,即两个人最近的共同祖先如果在五代以内(即本人.父母.祖父母.曾祖父母.高祖父母)则不可通婚.本题就请你帮助一对有情人判断一下,他们究竟是否可以成婚? 输入格式: 输入 ...
- 【开源库推荐】#3 Android EventBus的使用
原文地址:[开源库推荐]#3 Android EventBus的使用 | Stars-One的杂货小窝 EventBus的Github Event bus for Android and Java t ...
- Arduino uno r3 使用 ESP8266 UART-WiFi 透传模块
一.所需硬件材料 1.ESP8266:01s某宝上3.5块钱 2.杜邦线:某宝几块钱一组40P,这里只需要三根,用于连接 树莓派与继电器 3.烧录器 二.ESP8266 AT固件烧录 ESP8266主 ...
- Java-ASM框架学习-从零构建类的字节码
Tips: ASM使用访问者模式,学会访问者模式再看ASM更加清晰 什么是ASM ASM是一个操作Java字节码的类库 学习这个类库之前,希望大家对Java 基本IO和字节码有一定的了解. 高版本的A ...
- 2、使用HashOperations操作redis(Hash哈希表)
文章来源:https://www.cnblogs.com/shiguotao-com/p/10560458.html 方法 c参数 s说明 Long delete(H key, Object... h ...
- 单&双&单|双||的区别
System.out.println(true|true);//true System.out.println(true|false);//true System.out.println(false| ...
- P1759 通天之潜水(双写法+解析)
算法解析 动态规划问题满足三大重要性质 最优子结构性质:如果问题的最优解所包含的子问题的解也是最优的,我们就称该问题具有最优子结构性质(即满足最优化原理).最优子结构性质为动态规划算法解决问题提供了重 ...
- git连接远程仓库
1. 连接远程仓库 1.1. 创建仓库 在连接远程仓库之前,得先要确定你有一个远程仓库,到GitHub官网搞一个账户. 点右上角的加号然后"New repository"输入一个仓 ...