深入理解OSGI Equinox原理应用与最佳实践 知识整理篇

本文深入介绍了OSGI框架,特别是Equinox实现,讲解了OSGI的模块化、生命周期和服务层原理。重点阐述了bundle的元数据、生命周期状态和类加载器的工作方式。同时,通过Neonat项目展示了Equinox的实际应用,包括bundle的构建、持久化、控制台交互和用户管理等模块。此外,文章还讨论了服务注册、服务事件和动态服务监听的处理。
摘要由CSDN通过智能技术生成

1.OSGI框架是什么?

OSGI为实现Java模块化开发,实现热插拔功能化的框架实现。可以实现类似不重启系统也可以实现添加,删除其部分功能而不会导致系统崩溃的框架实现。一个功能相当一个模块,针对模块开发。增加删除功能,相当于模块的安装和卸载。

2.OSG组成部分的规范和原理

组成部分一般分为 OSGI运行环境,模块层,生命周期层,服务层,安全层。重点在于模块层,生命周期层,服务层。

2.1 模块层:

描述一个模块的元数据信息,执行环境,模块的导入,导出包,类加载顺序的内容,是OSGI框架最基本的组成。

2.2 生命周期层:

描述一个模块的安装,解析到环境,启动,更新,停止,卸载的过程,还有这些变化过程中的事件监听,上下文变化等处理。

2.3 服务层:

描述如何定义,注册,导出,查找,监听和使用OSHGI中的服务。

详细情况:

模块(bundle)关于/META-INF/MANIFEST.MF文件主要知道各个参数代表什么含义:
1.Bundle-ActivationPolicy: lazy 这个属性代表设置Bundle的加载策略是懒加载模式,即只有用到这个Bundle时它才会激活。
2.Bundle-Activator: com.acme.Activator 这个属性代表实现BundleActivator接口的start()和stop()方法控制bundle启动和停止时的行为动作的实现类路径
3.Bundle-Classpath: /jar/demo.jar 这个属性是用来声明bundle在加载引用类时,可以从哪些jar包中找到对应的类,和类加载过程有关,可能会失效()
4.Bundle-License: … 标记bundle的授权协议信息。
5.Bundle-Localization: OSGI-INF/110n/bundle 为bundle设置不同语言系统的本地化信息。
6.Bundle-ManifestVersion: 2 决定bundle遵守哪个版本的OSGI规范 R4/R5 值为2,R3 值为1。
7.Bundle-Name: base 代表bundle的名称,但是不是标识,只是用来生成jar文件名的,和版本号一起使用。
8.Bundle-NativeCode 代表用其他语言来写的代码的运行环境
9.Bundle-RequireExecutionEnvironment: … 设置bundle所需要的执行环境。
10.Bundle-SymbolicName:base 这个是标识,和版本号一起组成bundle的唯一标识。
11.Bundle-Version: 2.4.1 版本号 2代表有系统不兼容的重大功能更新,4代表要提供新的特性和接口会变,1代表接口没有变化,而只是改了下内部实现会变。和上面组成唯一标识。
12.DynamicImport-Package 动态导入,导出package
13.Export-Package 导出包
14.Import-Package 导入包
15.Provided-Capability 会提供的服务特性
16.Require-Capability 需要的服务特性
17.Require-Bundle 声明依赖其他Bundle,被声明依赖的bundle可以直接使用这个bundle导出的包,和Import-Package有区别,import可以更精确的过滤。

Fragment bundle是一种特殊的Bundle,是Bundle的插件,是模块中的模块,是不能独立存在,必须依附在Bundle上。

模块(bundle) 之间引用依赖,导入,导出等操作
1.用Import_Package和Export_Package处理导入,导出等操作可以加过滤条件限制其导入,导出类文件。

第一种 通过include和exclude来实现,只应用于导出,导入不做限制,因为导出已经限制了。例如只导出以IO开头,不以Impl结尾的类: Export_Package: org.service.io; include=“IO*”; exclude=“*Impl”

第二种 通过version 版本过滤 导出是代表具体的版本,而导入是代表版本的范围,[2.2.2,3.0.0) 代表导入的版本包含2.2.2版本到不包含3.0.0版本。直接写一个版本号代表包含这个版本和这个版本以上的版本都可以。例如:Export_Package: org.service.io; version=“3.0.0” 代表 导出的版本是3.0.0; Import_Package: org.service.io: version=“[2.2.2,3.0.0)” 代表 导入的版本是包含2.2.2 不包含3.0.0

第三种 通过 bundle-sybmbolic-name 和 bundle-version 这两个值来限制
导入模块的值和版本范围。

第四种 通过自定义属性filter来过滤 一般这个过滤不会强制执行,有对应filter属性才会去判断是否导入正确,如果要强制执行要添加 mandatory属性设置 例如:
导出设置: Export-Package: org.osgi.simple; filter=“true”
导入设置: 1. Import-Package: org.osgi.simple; filter=“false”
2. Import-Package: org.osgi.simple; filter=“true”
3. Import-Pachage: org.osgi.simple;
上述三种情况 2,3 情况满足,情况3 没有对应filter就不会去匹配判断。
如果导出强制添加 mandatory 属性 则只有情况2是正确的。
导出设置:Export-Package: org.osgi.simple; filter=“true”; mandatory=“filter”

第五种 可选导入resolution = “optional” 代表导入的包可以存在也可以不存在,动态导入DynamicImport-Package 标识只有用到时才会导入这个包,而不是启动时就去找是否有bundle提供对应的包。可选导入则是启动时就去找。

第六种 通过导出 uses 属性限制导出包内部其他包的版本等限制,即当导出包中包含其他包并限制版本等信息,那么其他bundle导入这个包时,也一定要满足其包含包的版本等信息。
例如:
Bundle A:
Export-Package: org.feng.simple; uses:=“javax.servlet.http”
Import-Package: javax.servlet.http; version=“2.4.0”
Bundle B:
Import-Package: org.feng.simple, javax.servlet.http
这种情况下bundle B中的 http这个包必须在2.4.0版本和以上的版本 否则会报错。

拆分包:当OSGI容器中包含两个或两个以上的Bundle导出名称相同的package时,容器会拆分包会使导入的过程变复杂
循环导出OSGI框架是通过记录每个bundle的状态,保证每个bundle只被搜索过一次。

OSGI框架类加载器的执行情况
对应java.*开头的类会使用父类加载器,使用双亲委派机制查找类并加载类,一般为(启动加载器,扩展加载器,应用加载器),没有找到会直接报错。
对应父类委派清单上先使用父类加载器查找,没有找到不会报错,会继续用导入包的类加载器去查找,还是没有找到会用本身bundle的类加载器查找,还是没有找到要看下模块是否有插件,有插件要到插件的bundle查找,还是没有找到会用动态导入的类加载器去查找,还是没有找到就会报错。
其他一般的类,不会用父类加载器查找,而是从导入包的类加载器开始,和上面类似的查找。

bundle生命周期的6种状态
1.uninstall 卸载状态 bundle已经移除OSGI容器
2.install 安装状态 bundle已经放进OSGI容器,还没有使用
3.resolved 解析状态 bundle的相关导入导出之间的依赖,版本等信息正确
4.starting 启动中 bundle在容器启动过程中的状态
5.stoping 停止中 bundle在容器停止过程中的状态
6.active 已激活 bundle在容器中已经可以随时使用

安装和卸载都是原子性的,要么成功,要么不成功
更新操作是先stop()方法再start()方法,但是由于旧的bundle对象不会直接被gc回收,所以还是可以调用旧的bundle对象。直到调用PackageAdmin的refreshPackage()方法或OSGI框架重启,才会删除旧的bundle对象。

bundle安装过程
根据manifest.mf 文件对其校验并获取对应内容信息,生成名称+版本唯一标识,通过标识判断容器中是否存在,存在不生成,不存在再通过bundle类型生成不同的bundle(例如bundle的插件等)再将这个生成的bundle对象放在容器对应仓库里,最后更新其状态为install状态。

bundle启动过程和停用过程
启动过程: 执行Activator.start()方法,这段时间都是starting状态,执行结束后为active状态。
停用过程: 执行Activator.stop()方法,这段时间都是stoping状态,执行结束后为resolve状态。

bundle的启动级别 startLevel
作用:全局控制bundle的启动,停止操作。每次加1的启动或停止等于启动级别的bundle。数字越大,启动越晚,停止越早。
场景:
1.安全模式,将重要,信赖的bundle的启动级别限制在某个阀值。后面扩展的bundle如果出错也不会影响到核心业务。
2.闪屏:当系统启动准备时间过长时,可以先设置一个欢迎画面给客户看。
3.模块优先级:将一些重要的先执行启动,而不重要的延迟启动,使系统更快的运行起来

事件监听是必须以事件发生那一刻的监听器快照作为事件分派目标,可以会出现事件监听器执行时,监听器不监听当前发生的事件的情况。要特别注意。所以要保证先注册监听器,再发生事件,才可以分发出去。(bundle上下文一定要存在,否则监听器就监听不到事件。)

服务层核心概念
1.service: 服务是一个普遍的java对象,实现一个或多个接口,并在服务注册表中注册,其他的bundle可以从服务注册表中找到并使用。
2.service register:服务注册表,是bundle的共享数据区域,保存服务对象的信息,例如服务属性,服务次数等信息。
3.service reference:服务引用,先获取引用,再获取服务实例。
4.service registration:bundle向服务注册表注册返回值,可以用来更新服务属性,注销服务等操作。
5.service event:服务事件,服务注册,修改,注销等操作时触发的事件。
6.service listener; 服务监听器,当服务事件发生时执行相对应的代码。

osgi控制台命令打help查看

对于相同接口不同实现的接口服务bundle有一下几种方式可以解决:
1.在bundle注册服务表时properties参数设置service.ranking 值,值越大,越先执行。
2.properties参数还有第二优先级service.id值,值越小,越先执行。
3.properties参数设置自定义参数service.type, 当获取服务引用时设置过滤 String filter=“service.type=B”,就只可以获取到服务设置自定义参数为B的服务引用。当获取服务引用时设置过滤 String filter=“service.type=*”,代表可以获取全部设置自定义参数service.type的服务。

为什么要先拿到服务引用,再去拿服务对象呢?因为动态性决定bundle的服务会随时卸载,安装等操作,拿服务对象会导致gc回收压力增大,容易内存溢出。

服务勾子
1.处理服务状态变化的EventListenerHook
当服务注册为EventListenerHook时,即实现EventListenerHook接口,实现接口event()方法,当服务有注册,修改,注销等操作时会自动调用event()方法,它的第一个参数时服务事件,第二个参数是以BundleContext为key,Collection < ListenerInfo > 为value 构成,表示服务事件将要分派到的事件监

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值