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 构成,表示服务事件将要分派到的事件监听器。可以移除Map集合中的数据来达到事件不分派给被移除的监听器中,但不可以添加,修改等操作只能删除。
2.获取服务请求的FindHook
即实现FindHook接口的find方法,其第一参数时BundleContext上下文,第二个参数是请求服务时传入的类/接口名,第三个参数是过滤条件filter,第四个是要不要查询全部的服务引用,第五个是最终返回的服务引用集合,这个参数可以移除其中一些数据来达到隐藏服务的目的,这个也只能删除操作。
3.服务监听器变化的ListenerHook
即实现ListenerHook接口的added和removed方法,只有一个参数listeners 代表服务添加或删除时的一组监听器。这个参数不能删也不能减,否则会抛异常。

*由于EventListenerHook和FindHook都是优先于服务请求和事件分派前执行的,所以可以当做防火墙使用,过滤掉一些不必要的请求和处理。

3.Equinox框架实战

3.1 Neonat 项目基本bundle搭建

base bundle 实体模块:

1.实体有论坛版面Board和其内部的帖子子实体Topic
2.注册论坛模型访问服务到服务注册表和统一实体持久化接口
3.Activator.start() 方法中注册论坛模型访问服务:

 public void start(BundleContext bundleContext) throws Exception {Activator.context = bundleContext;context.registerService(NeonatModelService.class, new NeonatModelServiceImpl(), null);}

persistener bundle 持久化模块:

1.通过对象序列化来实现base模块的持久化接口
2.注册持久化服务到服务注册表
3.Activator.start() 方法中注册持久化服务:

 public void start(BundleContext bundleContext) throws Exception {Activator.context = bundleContext;context.registerService(PersistenceService.class, new SerializePersistenceService(), null);}

console bundle 控制台命令行交互模块:

1.通过控制台管理器管理每个用户的交互控制台,再根据命令行处理对于方法。
2.注册控制台管理服务到服务注册表
3.Activator.start() 方法中注册控制台管理服务:

 public void start(BundleContext bundleContext) throws Exception {Activator.context = bundleContext;Thread thread = new Thread(new ConsoleManager(), "Neonat Console Manager");thread.setDaemon(false);thread.start();}

web bundle 浏览器方式交互模块

1.注册servlet Jsp 静态资源等信息
2.Activator.start() 方法中使用:

import javax.servlet.Servlet;
import org.eclipse.equinox.http.helper.BundleEntryHttpContext;
import org.eclipse.equinox.http.helper.ContextPathServletAdaptor;
import org.eclipse.equinox.jsp.jasper.JspServlet;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.service.http.HttpContext;
import org.osgi.service.http.HttpService;
import org.osgi.util.tracker.ServiceTracker;/*** 注册JSP和静态资源 路径*/
public class HttpServiceTracker extends ServiceTracker<HttpService, HttpService> {private BundleContext context;public HttpServiceTracker(BundleContext context) {super(context, HttpService.class, null);this.context = context;}@Overridepublic HttpService addingService(ServiceReference<HttpService> reference) {HttpService hs = context.getService(reference);// 注册/WebContent目录为Web根目录,这样可以访问里面的静态资源HttpContext hc = new BundleEntryHttpContext(context.getBundle(), "/WebContent");// 注册/WebContent里面的*.jsp文件,这样JSP页面才可用Servlet jspServlet = new JspServlet(context.getBundle(), "/WebContent");try {hs.registerResources("/", "/", hc);hs.registerServlet("/*.jsp", jspServlet, null, null);} catch (Exception e) {e.printStackTrace();}return super.addingService(reference);}
}
public class Activator implements BundleActivator {private static BundleContext context;private static HttpServiceTracker tracker;public static BundleContext getContext() {return context;}public void start(BundleContext bundleContext) throws Exception {Activator.context = bundleContext;// 根据bundle上下文初始化 http信息tracker = new HttpServiceTracker(context);tracker.open();}public void stop(BundleContext bundleContext) throws Exception {Activator.context = null;tracker.close();}
}

userAdmin bundle 用户管理模块:

1.注册jsp资源文件

 @Overridepublic HttpService addingService(ServiceReference<HttpService> reference) {HttpService hs = context.getService(reference);// 注册/WebContent里面的*.jsp文件,这样JSP页面才可用Servlet jspServlet = new JspServlet(context.getBundle(), "/WebContent");try {hs.registerServlet("/admin/userManager.jsp", jspServlet, null,new UserAdminAuthenticationHttpContext(hs.createDefaultHttpContext()));} catch (Exception e) {e.printStackTrace();}return super.addingService(reference);}

2.创建论坛的组织、角色和管理员基础用户,初始化页面管理权限
3.注册userAdmin服务

 /*** 在跟踪到OSGi容器中存在UserAdmin服务时创建基础结构*/public UserAdmin addingService(ServiceReference<UserAdmin> reference) {userAdmin = context.getService(reference);new OrganizationRoleManager(userAdmin).initiate();return super.addingService(reference);}

4.Activator.start() 方法中开启上面两个注册服务

 public void start(BundleContext bundleContext) throws Exception {Activator.context = bundleContext;userAdminTracker = new UserAdminTracker(context);// 开启userAdmin服务userAdminTracker.open();httpServiceTracker = new HttpServiceTracker(context);// 开启jsp注册服务httpServiceTracker.open();}public void stop(BundleContext bundleContext) throws Exception {Activator.context = null;userAdminTracker.close();httpServiceTracker.close();}

perferences bundle 持久化非事物性数据模块:

1.使用树状的数据结构储存数据实现base模块持久化接口
2.注册持久化服务到注册表
3.Activator.start() 方法注册进去:

 public void start(BundleContext bundleContext) throws Exception {Activator.context = bundleContext;context.registerService(PersistenceService.class, new PreferencesPersistenceService(), null);}

声明式服务 Component

1.出现的原因:
上面的注册服务到服务注册表上或从服务注册表中获取服务 都是通过编码或跟踪器来实现。当这种代码多时,系统整理容易出问题,所以衍生出DS容器来统一管理服务之间的交互关系。

2.DS容器怎么知道是不是一个component:
MANIFEST.MF元数据上的标记service-component来记录这个bundle包含哪些component。例如:Service-Component: OSGI-INF/CnService.xml,OSGI-INF/EnService.xml

3.Component的三种类型:
Immediate Component :只要自己依赖的服务都满足,component就立即转化为激活状态组件。没有写Service-Component或者对应xml文件写了 immediate=true 则是这种类型。
Delayzed Component: 默认为这种类型,即懒加载模式,即自己依赖的服务都满足时,component不会立即激活,执行其activate() 方法,而是其服务注册表上对应的服务被调用时才会激活执行activate()方法。
Factory Component : 用来满足创建多例服务场景

4.Component的生命周期:
已启用(Enabled)/已停用(Disabled)
满足依赖(Satisfied)/不满足依赖(Unsatisfied)
激活(Activated)/钝化(Deactivated)
Immediate Component的生命周期:一旦Component的依赖项得到满足,Component会立即启动激活过程,将Component配置中描述的服务注册到OSGI服务注册表中,执行自己的activate()方法把状态改成激活状态。同样Component依赖不满足时,会立即钝化,注销注册表上的服务,状态变成钝化状态。
Delayed Component的生命周期:因为是懒加载模式,所以当其依赖项都满足时,DS容器只是在OSGI服务注册表上注册了一个服务代理,并没有将服务对象生成出来,服务的实现类也可以不加载,这时候的状态叫做已注册。当发布的服务被真正调用时,DS容器还会根据Component配置的serviceFeFactory属性是否为true来决定Component实例是单例还是多实例。Component实例创建成功后,会调用activate()方法将状态变成激活状态。而注销的时机为这服务没有bundle使用时,DS容器会自动将其销毁,并退回为已注册的状态。
Factory Component的生命周期: 因为是工厂批量生成Component,所以DS容器会先注册一个对应工厂服务(ComponentFactory)。当依赖项都满足时会调用工厂服务的newInstance()方法的到ComponentInstance对象,再通过这个对象的getInstance()方法得到Component实例,成功后调用其activate()方法改变为激活状态。而钝化会调用ComponentInstance对象的dispose()方法来实现。

5.Component 上下文
一般用来获取Component属性(getProperties() 方法),获取服务(locateService() 方法),获取bundle上下文(getBundleContext() 方法)等操作

6.component 循环引用问题,将cardinality属性被设置为“必须”("1…1"或“1…n”)的服务改成可选的服务设置(“0…1"或"0…n”)即可。让循环依赖的可以以0个可选依赖先进入激活阶段来破除循环依赖的问题。

subsystems 服务

这个服务的作用时将一组bundle粘合到一起,做好资源隔离和共享,动态添加和删除组成的bundle。
1.Subsystem的格式:首先必须是一个标准的ZIP压缩文件,内部可以(不是必须)包含以下内容,1.零到多个资源,这些资源都以标准的OSGI Bundle或者其他的Subsystem压缩包,2.元数据定义文件:OSGI-INF/SUBSYSTEM.MF,3.部署信息定义文件:OSGI-INF/DEPLOYMENT.MF
MF文件和bundle的元数据文件属性类似的属性配置。
2.Subsystem的三种类型:
Application 是一种有资源范围限定的,策略是不会共享元数据配置文件导出包的任何资源,也不会共享元数据配置文件导出包的资源,但是内部bundle导入要的资源可以从其他的服务中导入进来。
Composite 也是一种有资源范围限定的,策略是只会使用元数据配置文件上列举的导入导出的资源。
Feature 是一种没有资源范围限定的,策略是内部的bundle导入和导出在Feature Subsystem 所在的区域都是共享的。

以上为个人记录。

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

  1. 前端代码标准最佳实践:HTML篇

    Web前端代码中,HTML是根本,CSS和JavaScript也是围绕着既有的HTML结构来构建,所以良好的HTML代码结构,除了提高了HTML代码的可读性,可维护性和执行性能之外,也可以让相对应的C ...

  2. Guava Cache 原理分析与最佳实践

    前言 在大部分互联网架构中 Cache 已经成为了必可不少的一环.常用的方案有大家熟知的 NoSQL 数据库(Redis.Memcached),也有大量的进程内缓存比如 EhCache .Guava ...

  3. 前端代码标准最佳实践:CSS篇

    上一篇<前端代码标准最佳实践:javascript>发表后,大家讨论还是很热烈,从侧面体现了前端工程师对写标准的前端代码的重视程度很高.这些最佳标准实践并不是那个权威组织发布的,而是由大量 ...

  4. IT项目管理最佳实践(管事篇)

    读书笔记: 序号 最佳实践 可归并入的过程组 可归并入的知识领域 1 在制定项目之前,应尽可能识别出项目干系人,特别是项目的主要干系人 启动 项目干系人管理 2 在项目正式开工之前,应该和项目各方商定 ...

  5. 前端代码标准最佳实践:javascript篇

    2019独角兽企业重金招聘Python工程师标准>>> 前言 最近一直重构项目的前端代码,也参考了各种前端代码的最佳实践,目的是让前端的HTML,CSS,Javacript代码更符合 ...

  6. 《深入理解Android:Telephony原理剖析与最佳实践》一1.3 Android Telephony框架结构...

    1.3 Android Telephony框架结构 前面对Android手机操作系统整体框架结构及每一层进行了简单的分析和说明,相信大家对Android智能手机操作系统有了一些基本的了解和认识.结合前 ...

  7. 《深入理解Android:Telephony原理剖析与最佳实践》一1.1 智能手机的系统结构

    1.1 智能手机的系统结构 Android手机的基本硬件结构是符合智能手机的基本硬件结构,我们要学习Android移动开发,首先需要了解智能手机的硬件系统基本结构. 随着通信领域的快速发展,移动终端发 ...

  8. 系统封装工具_去工具化/脚本化理解,自动化运维落地最佳实践之业务/架构/模型/方法...

    本文转载自:互联网运维杂谈 近年来后端IT也呈现更复杂的形态,底层IT架构逐渐开放平台化.云化,上层应用微服务化等等,虚拟化.云平台.容器PaaS和云原生框架都进入到IT运行环境中,而传统业务依然运行 ...

  9. 【送书福利-第七期】《分布式中间件核心原理与RocketMQ最佳实践》

    大家好,我是洲洲,欢迎关注,一个爱听周杰伦的程序员.关注公众号[程序员洲洲]即可获得10G学习资料.面试笔记.大厂独家学习体系路线等-还可以加入技术交流群欢迎大家在CSDN后台私信我! 本文目录 一. ...

最新文章

  1. python pip国内镜像
  2. shell中cut-b_Shell中cut用法
  3. 数据库系统概念总结:第八章 关系数据库设计
  4. C语言 | 求1000以内的所有完数及求2000以内最大的完数(C源代码)
  5. javascript的内置对象以及BOM(定时器,location)
  6. 印刷点阵字体_印刷术如何确定可读性:衬线与无衬线,以及如何组合字体。
  7. 一个application多个 URL
  8. mysql怎么看实例名_南方“中看不中吃”的前4名水果,莲雾只是垫底,你怎么看?...
  9. DataGridView实现多维表头
  10. Python+pandas+matplotlib数据分析与可视化案例(附源码)
  11. 矩池云解决方案介绍图
  12. 服务器powershell占用百分百,使用PowerShell统计服务器C盘空间
  13. 第1篇:熊猫烧香之手动查杀
  14. FlashPaper组件——api
  15. android地图方位角_Android获取经纬度,计算距离,方位角
  16. java 生成根据图片内容生成图片包含格式有【png jepg GIF tiff wbmp】
  17. ubuntu 安装浏览器flash插件
  18. springboot+vue项目部署到外网服务器的完整步骤(前后端分离 分别部署)
  19. 水果店节日活动营销方案,水果店如何做营销活动
  20. iOS开发:关于UILabel、UIButton、UITextField文字下划线的设置方法(涉及到富文本的知识)

热门文章

  1. usb万能驱动win7_最新电脑为何不支持安装win7系统?原来秘密就藏在这里
  2. 戴尔笔记本——减低风扇声音的一种办法
  3. Linux 命令大全(看这一篇就足够)
  4. matlab 机床,基于MATLAB的数控机床控制系统的设计及仿真
  5. 4.SolidWorks中的工程图,出图时公差怎么标?
  6. 【UML】-- 用例图练习题含答案(订餐系统、远程网络教学系统、交互式网络系统)
  7. asp.net页面名首字母大小写对网页权重的影响
  8. Redis五大数据类型与使用场景汇总!!(含完整实战案例,建议收藏)
  9. Gitee如何免费部署静态网站?
  10. ig夺冠后服务器不稳定,LOL道歉声明:没有不重视IG 只因夺冠当晚服务器太卡