深刻理解OSGI的模块化

 

定义

OSGIOpen Service Gateway Initiative)技术是面向Java的动态模型系统。java

OSGI框架实现了一个优雅、完整和动态地组件模型。应用程序(bundle)无需从新引导能够被远程安装、启动、升级和卸载。数组

OSGi技术提供容许应用程序使用精炼、可重用和可协做的组件构建的标准化原语。 这些组件可以组装进一个应用和部署中。安全

OSGi服务平台提供在多种网络设备上无需重启的动态改变构造的功能。服务器

为了最小化耦合度和促使这些耦合度可管理,OSGi技术提供一种面向服务的架构,它能使这些组件动态地发现对方。    网络

OSGi联盟已经开发了例如像HTTP服务器、配置、日志、安全、用户管理、XML等不少公共功能标准组件接口。这些组件的兼容性插件实现能够从进行了不一样优化和使用代价的不一样计算机服务提供商获得。然而,服务接口可以基于专有权基础上开发。架构

OSGi的主要职责就是为了让开发者可以建立动态化、模块化的Java系统。框架

OSGI框架

 

模块和模块化

模块(module):定义了一个逻辑边界,这种模块自己精确的控制了哪些类是彻底被封装起来的,而哪些类须要暴出来做为外部使用。ide

模块化(modularity):将一个大型系统分解为多个较小的互相协做的逻辑单元,经过强制设置模块之间的逻辑边界来改善系统的维护性和封装性。模块化

构成

OSGi框架从概念上能够分为三层:模块层、生命周期层和服务层。优化

Module Layer:模块层关注代码的打包和共享;

Lifecycle Layer:生命周期层提供运行时管理以及对OSGI框架的访问接口;

Service Layer:服务层关注模块之间的交互和通讯。

 

模块层

模块层是 OSGi 框架中最基础的部分。

OSGi 的模块化,是经过为 Jar 包添加metadata 来定义哪些类该暴露,哪些类该隐藏,其控制单元叫作 Bundlejar 包)。

首先,必须先了解一个基本概念——什么是Bundle

bundle 是以 jar 包形式存在的一个模块化物理单元,里面包含了代码,资源文件和元数据(metadata),而且jar包的物理边界也同时是运行时逻辑模块的封装边界。

 

如何定义Bundle

 

Bundle OSGi 中的基本组件,其表现形式仍然为 Java 概念中传统的 Jar 包。

经过 META-INF 目录下的 MANIFEST.MF 文件对其予以进一步的定义。

一般一个 MANIFEST.MF 文件的内容以下:

 

Manifest-Version: 1.0

Bundle-ManifestVersion: 2

Bundle-Name: Util

Bundle-SymbolicName: com.ibm.director.la.util

Bundle-Version: 1.0.0

Bundle-RequiredExecutionEnvironment: J2SE-1.5

Import-Package: org.osgi.framework;version="1.3.0"

Export-Package: com.ibm.director.la.util;uses:="org.osgi.framework"

Bundle-ClassPath: lib/junit.jar,

 

MANIFEST.MF 文件存储的其实是 Bundle 的元数据。

元数据的内容能够精确的定义 Bundle 的各类特征,同时能更好的对 Bundle 进行标识同时帮助用户对Bundle进行理解。

 

MANIFEST.MF文件格式

1.         属性声明的通常格式:namevalue

2.         一行不超过72个字符,下一行继续则由单个空格字符开始

3.         每一个子句(clause       进一步分解为一个目标(target)和一组由分号分隔的name-value对参数(parameter

 

元素解释:

Bundle-SymbolicName  惟一的bundle名称,至关于在系统中的idsingleton表示是否使用单启动方式 #可选的

 

Bundle-Version  主要的版本号

Bundle-ManifestVersion  定义了bundle遵循规范的规则,1表示r3规范 2表示r4和之后的版本

a)         惟一有效的值是2

b)         没有Bundle-ManifestVersionBundle不要求指定Bundle- SymbolicName属性

 

Bundle-Name   bundel名称

Bundle-Vendor  发布商

Bundle-RequiredExecutionEnvironment  须要的执行环境

Build-Jdk  jdk版本

Created-By  建立者

Bundle-Activator  Activator类路径

Import-Package  引用包的信息,包括包名称和版本号,只有引用了这些包,才能让classloader装载

a)         导入一个包并无导入它的子包

b)         Import-Package经过属性导入特定的包

c)         java.*

d)         对于任意属性,OSGI只支持相等匹配

e)         Version及其值的格式是OSGI规范所定义,支持更加灵活的匹配方法

 

f)          须要指定一个精确的版本范围,使用“[1.0.1,2.0.1]”这样的格式

g)         当没有指定版本范围时,默认的值是“0.0.0

 

Export-Package  对外暴露的Package

a)         标准Jar文件默认公开一切内容,而Bundle中默认不公开任何内容

b)         可导出多个包,用逗号分隔

c)         能够给导出包增长任意属性

d)         能够给导出包设置Version,默认为0.0.0

        

 

Require-Bundle  直接引用整个bundle

Bundle-ClassPath  Bundle Classpath,内部类路径

Fragment-Host  Fragment 类型 Bundle 所属的 Bundle

DynamicImport-Package  Bundle动态引用的 package

 

OSGI类的查找顺序

1.         若是类所在的包以“java.”开头,委托给父类加载器

2.         若是类所在的包在导入包中,委托给导出该包的Bundle

3.         Bundle自身的类路径上查找

 

依赖解析

含义

1.         只有知足全部的依赖(Import-Package),bundle才可用

2.         OSGI框架的一个最重要任务之一就是:经过自依赖解析自动化地进行依赖管理

 

依赖解析规则

1           级联解析

2           Import-Package的属性约束和版本约束

3           多个Bund知足Import-Package依赖(多个Provider)时:

3.1     已解析的(resolvedbundle优先级高,未解析的(installedbundle优先级低

3.2     相同优先级,有多个匹配时,版本高者优先,版本相同则选最早安装的bundle

4           一个bundle只能看到某个package的惟一一个实例

5           uses 子句

5.1     用于限制Export-Package

5.2     须要用到uses子句的场景

5.2.1     导出包中的类,其方法签名中包含了其Import-Package中的类

5.2.2     导出包中的类,继承了其Import-Package中的类

5.3     users约束是可传递的

5.4     谨慎使用uses,大大·限制解析的灵活性

 

生命周期层

做用

1           在应用程序外部,生命周期层精确低定义了对bundle生命周期的相关操做

2           对生命周期的操做,容许你动态地改变进行于框架中的bundle组成,并以此来管理和演化应用程序

3           在应用程序内部,生命周期层定义了bundle访问其执行上下文的方式,为bundle提供了一种与OSGI框架交互的途径以及一些执行时的便利条件

4           OSGI框架支持对bundle形式的JAR文件实现全生命周期管理,包括:安装、解析、启动、中止、更新和卸载

5           运行时生命周期管理,“动态类路径”

 

下图为 Bundle 生命周期的状态转移图:

 

 

重要接口

生命周期层的API主要是由如下三个核心接口来组成的:

BundleActivatorBundleContext Bundle

 

BundleActivator:让你可以捕捉bundlestartstop事件,并对这两个事件做出自定义的反应。

其中:

1           调用start()方法的激活器实例与调用stop()的实例是同一个

2           stop()方法被调用以后,激活器实例就被丢弃并再也不不用

3           若是一个bundle被中止后,又从新启动,那么将建立一个新的激活器实例,同时它的start()方法和stop()方法也将被适时触发。

 

BundleContext:一个bundle在框架中的执行时上下文,这个上下文提供了和框架进行交互的方法。

其中:

1           bundle属于active状态时,BundleContext才有意义,即start()方法被调用和stop()方法被调用之间的时间点

2           注册服务

方法以下:

public ServiceRegistration registerService(String clazz, Object service,

                            Dictionary properties);

 

调用例子:

@Override

    public void start(BundleContext context) throws Exception {

        Dictionary<String, String> props = new Hashtable<String, String>();

        props.put("ServiceName", "Calculation");

        context.registerService(ICalculation.class.getName(), new Calculation(), props);

        System.out.println("Service registered!");

    }

3           获取服务

有几种方式:

1ServiceReference ref = context.getServiceReference(LogService.class.getName());

优势:很难说有什么优势,硬要说几句的话,那就是逻辑够简单,调用最少,适合一次性操做。
缺点:须要判断返回值是否为null,须要手动申请和释放service,因为OSGi的动态性,请在获取ref后尽快使用,没法保证ref长期有效。每次访问都会有service获取和释放的开销。
用途:适合于不频繁的调用service,且在service不可用时也能继续执行后续操做的场景。

 

2使用ServiceListener

优势:只在Service变动时产生一次service获取开销,动态感知service的注册和注销。
缺点:在ServiceListener注册以前已经存在的Service没法监听到。须要本身维护service的获取和释放。在须要监听多个Service实例时,使用并不方便。

 

3使用ServiceTracker

ServiceTracker实际上是对ServiceListener实现方式的封装,使得对service的获取更加简洁,同时也解决了不能监听到已经存在的Service的问题(其实就是在增长ServiceListener的同时调用BundleContext.getAllServiceReferences方法以获取现有的Service引用)

 

有一点须要注意的是,tracker须要调用open方法才能监听到Service,另外,在bundle stop之后,bundleopenServiceTracker不会自动关闭,因此必定不要忘记在bundle结束以前,关闭全部在bundleopenServiceTracker

 

4、使用OSGI Blueprint

以下

 

 

 

Bundle:在逻辑上表示了一个bundleOSGi环境中的一个物理bundle对应了一个bundle对象。该对象中包含了bundle的基本信息和bundle生命周期的控制接口。

 

 

启动级别

1、启动级别的数值越高,启动顺序越靠后

2、只有System Bundlebundle ID0)的启动级别能够为0,其余Bundle的启动级别都大于0,最大值为Integer.MAX_VALUE

3、动态启动级别

 

系统Bundle

启动过程:Bundlestart()方法为空操做,由于OSGI框架一启动。系统Bundle就已经启动了

中止过程:Bundlestop()方法会当即返回并在另一条线程中关闭OSGI框架

更新过程:Bundleupdate()方法会当即返回并在另一条线程中重启OSGI框架

卸载过程:系统Bundle没法卸载,若是执行了Bundleuninstall()方法,那么框架会抛出一个BundleException异常

 

Bundle刷新流程

从某一bundle开始计算受影响的bundle有向图

处于Active状态的bundle被中止并被切换至Resolved状态

处于Resolved状态的bundle,切换至Installed状态,这些bundle的依赖关系再也不被解析

处于uninstalled状态的bundle会被从图中移除,同时也会被完全地从框架中移除(由GC回收)

其余bundle,若是框架重启以前处于Active状态,重启前框架会对这些bundle以及其所依赖的bundle进行解析

当全部的工做完成以后,框架会触发一个FrameworkEvent.PACKAGES_REFRESHED事件

 

服务层

面向服务的设计

1、下降服务提供者和使用者之间的耦合,这样更容易重用组件

2、更强调接口而不是实现类

3、清晰描述依赖关系,让你知道一切是如何结合在一块儿的(能够有附加的元数据描述)

4、支持多个相互竞争的服务实现,这样你能够互换这些实现(动态替换)

 

OSGI服务

OSGI框架拥有一个集中的服务注册中心,它遵循发布-查询-绑定模型

1、提供者bundle能够将POJOs发布为服务。

1.1、注册的时候能够设置这个 Service 的属性。而在获取 Service的时候能够根据属性进行过滤。

1.2、为了让别的bundle能发现这个服务,你必须在发布它以前对其进行特征描述。这些特征包括接口的名字(能够是名字的数组),接口的实现,和一个可选的java.util.Dictionary类型的元数据信息。

2、使用者bundle能够找到并绑定服务

3、服务注册、更新、注销

4、服务注册对象是私有的,不能被别的bundle共享,它们与发布服务的bundle的生命周期是绑定的

5OSGI将会接受以具体类名注册的服务,可是不推荐这样作

6、当一个bundle中止时,任何没有被移除的服务都会被框架自动移除。当bundle中止时,没必要明确地注销服务。

7、服务监听

8、服务追踪器 – ListenerServiceTracker

9、服务工厂 为不一样的bundle提供相同服务的不一样实例

10、配置管理:可将配置文件放置/etc下,随着配置文件的更改,ManagedService接口的实现类的updated方法也会被调用。

相关文章
相关标签/搜索