本文是osgi实战一书的前几章读书总结

1.  OSGi简介

Java缺少对高级模块化的支持,为了弥补Java在模块化方面的不足,大多数管理得当的项目都会要求建立一整套技术,包括:

  • 适应逻辑结构的编程实践;
  • 多个类加载器的技巧;
  • 进程内部组件间的序列化;

OSGi服务平台是专门针对Java对模块化支持不足的情况,由OSGi联盟定义的一个行业标准。OSGi服务平台引入一个面向服务的编程模型,被称作“VM中的SOA”。

OSGi是Java平台的一个模块化层,在计算机科学里,软件应用程序的代码被分割为表示独立内容的逻辑单元。

1.1       Java模块化的不足

Java以面向对象的方式提供了某种程度的模块化,但它未曾考虑支持粗粒度的模块化编程。

底层代码的可见性控制

虽然Java提供了很多控制可见性的访问修饰符(public,private等),但这些都是为了解决低层面向对象封装,而不是逻辑系统划分。Java用包来划分代码,如果需要代码在多个包可见,那么包内的代码必须为public。有时应用程序的逻辑结构需要特定的代码分属不同的包,但这也意味着任何包间的依赖都必须声明为public,这会暴露实现细节,使后续升级更加困难。

这意味着在使用Java语言,必须要面向以下的选择:

l  为了避免暴露非公有API而把不同的类打包在一起,以致损害程序的逻辑结构;

l  为了保持程序逻辑结构而使用多个包,代价就是暴露非公有API,被不同的包中的类访问。

易错的类路径概念

Java平台也会妨碍好的模块化实践,罪魁祸首就是类路径。类路径会隐藏代码版本,依赖和一致性等特性。类路径不关心代码版本——只返回找到的第一个版本,即使关注代码版本,但是还是无法明确描述依赖关系。建立类路径的过程是烦琐易错的,你只是不停地添加类库,直到虚拟机不再报出找不到类的错误。

部署和管理支持的不足

在Java中存在对多个版本的依赖时,没有简单的方法来正确部署这些代码并执行。在部署之后更新应用和组件也会面临同样问题。

1.2    OSGi架构

OSGi服务平台由两部分组成:OSGi框架和OSGi标准服务。

OSGi技术开始被应用到各个地方,Equinox OSGi框架实现是Eclipse IDE的底层运行时环境,如果你使用Glassfish作为应用服务器,其实你也是在使用OSGi,因为其框架实现是它的运行时环境。OSGi存在三个概念层。

  • 模块层——关注于打包和共享代码。
  • 生命周期层——关注于提供执行时模块管理和对底层OSGi框架的访问。
  • 服务层——关注于模块,特别是模块内组件间的交互和通信。

像常见的分层架构一样,OSGi的每一层都依赖于它的下层,服务层依赖于生命周期层,生命周期层依赖于模块层。

2.  精通模块化

2.1       什么是模块化

模块层是OSGi其他层的寂静处,模块化确切的定义是什么?简而言之,就是利用一组逻辑上独立的组件集合设计出完整的系统,这些逻辑上独立的组件被称为模块。

模块定义了强制性的逻辑边界:代码要么是模块的一部分,要么不是模块的一部分。模块内部的实现细节只对模块内部的代码可见,而其他代码只能看到模块明确公开的部分。模块的这个特定使得它可以作为设计逻辑结构必不可少的组成部分。

Java类之间的这些逻辑关系只能通过细看底层代码或者反复试验的方式才能弄清楚。模块封装了类,使得你可以表达应用中类之间的逻辑关系。所以,尽管面向对象和模块化提供了相同的功能,但是它们从不同粒度层次来实现这些功能。

使用Java进行开发时,可以把面向对象看作是模块的实现方式。像这样开发类时,你是在微观编程,这意味着你并不是考虑应用的整体结构,而是考虑具体的功能。在把相关类逻辑地组织成模块后,开始进行宏观编程,这意味着你需要关注于更大的系统逻辑组件和这些组件之间的关系。

模块是一个从逻辑上封装实现类的集合,一个基于实现类子集的可选公共API,以及一个对外部代码依赖关系的集合。

2.2       为什么使用模块化

从传统意义上来说,编程语言属于逻辑模块化机制,而操作系统和部署打包系统则属于物理模块化机制。Java使两者的差别变得模糊,因为Java既是一种编程语言也是一个平台。最终,随着应用规模的逐渐增长,模块化成为了控制应用复杂度的重要手段——分而治之。

内聚 用于衡量一个模块的类相互关联的程度,以及共同完成模块既定功能的紧密程度,应该尽量使得你的模块高内聚。

耦合 与内聚相反,是指不同模块束缚或者依赖于其他模块的程序,应该尽量降低模块之间的耦合度。

2.3       bundle

如果使用OSGi技术,必定会对bundle非常熟悉。bundle是OSGi对其模块化概念具体实现的定义。bundle是一个模块化的物理单元,以Jar文件形式包含代码、资源和元数据,其中Jar文件的边界也作为执行时逻辑模块化的封装边界。

bundle Jar文件和普通Jar文件的主要区别是模块元数据。OSGi框架使用元数据来管理它的模块化特征。由bundle创建的逻辑边界为bundle内部的类赋予不同于外部代码的可见性规则,这意味着在bundle JAR文件内部的public类不必对外部可见。通过模块私有的可见性逻辑边界有效地扩展了标准Java访问修饰符(public, private, protected和包私有)。

在使用元数据对bundle进行定义时,元数据提供了以下几条有关bundle的信息:

可读信息——纯粹是为了给使用bundle的人提供帮助的可选信息;

bundle识别——识别bundle的必要信息;

代码可见性——定义哪些代码内部可见和哪些代码外部可见的必要信息。

2.4      OSGi中的类搜索顺序

bundle在执行时需要一个类,OSGi框架将按照以下顺序搜索该类。

  1. 如果包含该类的报名以java.开头,当前类加载器的父类加载器会去搜索这个类。如果找到这个类,就使用它,如果没有找到,搜索以异常结束。
  2. 如果这个类是bundle的导入包中的类,OSGi框架从导出bundle中搜索这个类。如果找到了,就使用它,如果没有找到,该搜索以异常结束。
  3. 从bundle的类路径中搜索这个类。如果找到这个类,就使用它。如果没有找到,该搜索以异常结束。

2.5       OSGi依赖解析

使用Bundle-Classpath标签描述组成bundle的内部代码,使用Export-Package标签公开内部代码用于共享,以及使用Import-Package声明对外部代码的依赖。bundle元数据中的Export-Package和Import-Package构成了OSGi bundle依赖模型的主干,这是bundle之间包共享的前提。

使用bundle之间,它的依赖关系必须由框架来解析。将给定bundle的导入包与其他bundle的导出包进行匹配,以一致的方式匹配,任何给定bundle只能访问同一个版本。

当一个bundle试图去使用其他bundle时,框架会自动解析这个被使用的bundle。为了进行bundle解析,所有的bundle都必须安装到框架中。

在执行时,每个OSGi bundle都有一个与之关联的类加载器,这个类加载器使得bundle可以获得其有权访问的所有类。当导入bundle连接到导出bundle时,导入bundle的类加载器会得到导出bundle类加载器的引用,因此导入bundle可以委托导出bundle的类加载器去请求导出包中的类。

3.  生命周期

如果要使用bundle,你需要将它安装至OSGi框架的一个运行实例中。利用OSGi模块化特性可以从两方面入手,一方面是创建bundle,另一方面是借助OSGi框架在运行时管理并执行bundle。可以说,仅当将bundle安装至OSGi框架的一个运行实例中,该bundle才可用。

3.1    生命周期管理

OSGi生命周期层提供了一个管理API,以及一个定义明确的生命周期,面向OSGi框架中执行的bundle。生命周期层有两种不同的作用:

  • 在应用程序外部,生命周期层精确地定义了对bundle生命周期的相关操作,运行动态地改变运行于框架中的bundle的组成,并以此来管理和改进你的应用程序;
  • 在应用程序内部,生命周期层定义了bundle访问其执行上下文的方式,为bundle提供了一种与OSGi框架交互的途径以及一些执行时的便利条件。

生命周期层允许你在执行应用程序时从外部安装、启动、更新、停止以及卸载不同的bundle,进而定制应用的配置。一般来说,程序会显示或隐式地受到某种形式的生命周期的约束。

生命周期层API由三个主要接口构成:BundleActivator,BundleContext,Bundle。

3.2    OSGi bundle的生命周期

OSGi的生命周期层提供了使用bundle的途径,是bundle真正发挥作用的地方。为了使用bundle,需要同OSGi的生命周期层交互。模块层依赖于元数据,但生命周期层不同,它依赖于API。

需要特别注意的是,OSGi核心框架没有指定特定的机制来实现与生命周期层API的交互,其核心只是纯粹的Java API。

在标准的Java程序中,为了使用Jar文件,需要将其放置到类路径中。这与使用bundle的方法不同。bundle只有被安装到OSGi的框架的运行实例中才能被使用。OSGi框架支持对bundle形式的Jar文件实现全生命周期管理,包括安装、解析、启动、停止、更新和卸载。

OSGi是围绕普通Java线程抽象设计的,不同于其他重量级框架,它假设开发者自己做线程管理。OSGi运行库是线程安全的,在给出保证的同时,回调一般就被完成。

当bundle被启动时,框架调用其Activator的start()方法,当它被停止时,则调用stop()方法,这两个方法都会接收BundleContext接口的一个实例。

bundle上下文对象

bundle上下文对象是与之关联的bundle的唯一执行时上下文环境,只有当bundle处于激活状态时,才是有效的。更明确地说是指从Activator的start()方法被调用,一直到stop()方法结束的一段时间内。BundleContext应该被视为敏感的或私有的对象,并且不能在bundle之间自由传递。

在执行时,框架由一个标识符为0的bundle表示,该bundle成为系统bundle,当框架运行时它会一直存在,无需安装。系统bundle遵循与普通bundle一样的生命周期,所以可以像操作普通bundle一样操作它。但当对系统bundle执行生命周期操作时,与普通bundle相比有着其特殊的意义。比如当停止系统bundle时,将会以友好的方式关闭框架。

生命周期状态图

bundle生命周期入口是BundleContext.installBundle()操作,该操作会创建一个处于INSTALLED状态的bundle。系统要保证在使用某个bundle之前,其依赖的所有bundle都是存在的,从INSTALLED到RESOLVED状态的转换提供了这种保证。除非所有依赖都已经满足,否则框架是不会允许bundle转换到RESOLVED状态的。

从STARTING到ACTIVE状态的转换是隐式的。当执行bundle Activator的start方法时,该bundle就处于STARTING状态,如果start方法执行成功,转换为ACTIVE状态,否则恢复至RESOLVED状态。

处于ACTIVE状态的bundle可以被停止,这会导致其状态经由STOPPING退回到RESOLVED,一个停止的bundle会回到RESOLVED状态而非INSTALLED状态。刷新或更新一个bundle将使其状态退回到INSTALLED。

一个INSTALLED状态的bundle可以被卸载,并且其状态转换到UNINSTALLED。如果要卸载一个处于激活状态的bundle,框架首先自动停止该bundle,使其状态转换为RESOLVED状态,然后在卸载该bundle之前将其状态转换为INSTALLED状态。

事件监听

OSGi是一个动态的执行环境。在创建bundle,以及最终的应用时,为了实现足够的灵活性,不仅要处理动态性,我们还需要注意OSGi框架在执行时发生的变化。OSGi支持两种类型的事件:BundleEvents和FrameworkEvents,前者报告bundle生命周期的变化,后者报告与框架相关的事件。

生命周期与模块化

OSGi中生命周期与模块层之间存在一种双向关系。生命周期层会对安装到框架之中的bundle进行管理,会影响到模块层对bundle之间依赖的解析;模块层使用bundle元数据,确保bundle在被使用之前它的所有依赖都已满足。

在bundle加载类之前,框架会完成对bundle的解析。当解析给定的bundle时,框架会解析另一个bundle,以实现对给定bundle的依赖解析。通过包管理服务(Package Admin Service),可以管理,查询bundle的状态,并解析bundle和处理依赖。

生命周期层允许部署并管理自己应用的bundle,当进行刷新bundle时,是一个两步的过程。新版本的bundle被放到正确的位置,但旧版本的bundle仍然存在,这样依赖它的bundle可以继续从中加载类。

4.  学习服务

服务是指提供者及其使用者之间的一个契约,使用者不关心服务的具体实现,只要遵守约定的契约即可,服务在一定程度上可被替代。

使用服务背后的动力是让别人为你工作,而不是试图自己做一切事情,委托的思想与面向对象的设计技术不谋而合。面向服务的设计方式推荐:

  • 降低服务提供者和使用者之间的耦合,这样更容易重用组件;
  • 更强调接口而不是实现;
  • 清晰描述依赖关系;
  • 支持多个服务实现,可以互换这些实现;

决定什么时候该使用服务的最好方式是要考虑这些好处:降低耦合、接口编程、附加的元数据和多种实现。明显用到服务的地方是在多个主要组件之间,特别是想更换或升级某些组件,但又不想在以后重写应用程序的其他部分时。

4.1       OSGi服务实战

一个bundle被发现并且开始使用OSGi中的服务后,服务可能随时消失,提供服务的bundle已经停止甚至卸载,无论什么原因,都应该做好准备应付服务的随时来去。

OSGi拥有一个集中的服务注册中心,遵循发布——查询——绑定模型。提供服务者bundle可以将POJOs发布为服务,使用服务者bundle可以找到然后绑定服务。

想要在OSGi中发布一个服务,需要提供一个接口名称,服务实现和可选的属性配置,当一切就绪,就可以通过使用bundle上下文来发布服务,BundleContext.registerService…

注册中心返回已发布的服务注册对象,可以用它来更新服务元数据或从注册中心移除服务。服务注册对象是私有的,不能被其他bundle共享。

当一个服务发布以后,随时可以通过在服务对象上修改其元数据(setProperties)。此外,调用unregister()方法还可以移除一个服务

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

通过BundleContext.getServiceReference方法从服务注册中心返回一个服务引用,这是已发现服务的间接引用。为什么注册中心返回的是一个间接引用,而不是实际的服务实现?这是因为OSGi中服务是动态的,注册中心必须将服务的使用与它的实现分离开,使用间接引用,注册中心能够追踪和控制对服务的访问,支持延迟,而且当服务移除时通知使用者。

如果有多个服务与给定的查询匹配,框架会选择它认为最佳的服务,可以使用Rank来判断最佳服务,较大数值表明排名更高。如果多个服务Rank相同,选择服务ID最小的服务。此外,bundle上下文提供了另一种查询方法,接受标准的LDAP过滤字符串,并可以返回所有与过滤器相匹配的服务。

4.2       处理动态性

注意,通常在OSGi中,当对服务使用方法调用时,将获得Java对象的一个引用,该引用由服务的bundle提供。因此,当指向服务实例的引用变量使用完之后,需要将其赋空以使得它可以被安全地执行垃圾回收。真正的服务实现不应该被保存为长期的变量,如成员变量,应该试着通过服务引用临时访问它,并预计到服务可能随时消失。

OSGi框架为服务事件提供了一个简单但非常灵活的监听器API,对于服务而言,存在三种不同类型的事件:

REGISTERED                             服务注册;

MODIFIED                                服务元数据被修改;

UNREGISTERED     服务处在被注销的状态中;

服务监听器想要接收服务事件,必须实现接口:ServiceListener,如何向框架注册服务监听器呢,使用BundleContext,它定义了添加和移除服务监听器的方法。服务监听器减少了对服务中心的频繁轮询,可以使在服务发生变化时立即作出反应,并且规避了查找然后获取方法获取服务时的固有竞态条件。在监听器层需要写大量的代码,而且必须为每个想要使用的服务写监听器底层代码,为什么OSGi不提供一个公用的工具类来执行这个操作?OSGi的确提供了这么一个类:ServiceTracker。

OSGi ServiceTracker类提供了一个安全的方法,方便地获得服务监听器的好处,通过ServiceTrackerCustomizer接口通过拦截被追踪的服务实例,提供了一个安全的增强追踪器功能的方法。

与服务监听器类似,定制器也基于服务生命周期的三个主要事件:添加、修改和删除。

5.  组件模型和框架

在Java开发中面向组件的方法变得非常受欢迎,比如EJB,Spring Beans,SCA等,OSGi技术为整合现有的或定义新的组件方法提供了一个坚实的基础。

5.1      面向组件

组件技术的一个重要方面是,它们描述的功能构建块比我们通常所说的对象功能构建块的粒度更粗。构建块之间通过接口提供功能,给定方法的组件通常根据组件模型定义的特定模式进行编程,组件框架用来执行组件。

长期以来,借助面向组件我们一直可以把已有的、可重用的组件搭建在一起,从而简单快速地创建应用。遵循组件模型可以带来很多好处,它促进了关注点的分离和基于接口方法的封装,限制对实现细节的依赖,提高了代码的可重用性。

组件模型通常明确地给出组件提供的接口和请求的接口,最终创建可重用性更好,能够组合的软件。

5.2       OSGi与组件

OSGi的核心规范定义了一个组件模型和框架用来运行相应组件,OSGi开发者其实就是组件开发者,OSGi核心规范所定义的组件模型名为面向服务的组件模型。OSGi组件模型可以被等同地理解为是带有组件的bundle和带有组件接口的服务。

对于一个bundle来说,要成为一个组件,需要实现一个bundle Activator,并支持生命周期管理。组件独立的部署单元为bundle的JAR文件。OSGi中逻辑上的bundle组件等同于物理上的bundle JAR文件。

包含在bundle里面的组件生命周期遵从它们包含的bundle,这意味着只有当包含它们的bundle处于启动状态时,才能够进入启动状态,一个独立组件的生命周期基于其自身的依赖关系和制约因素。

OSGi学习-总结的更多相关文章

  1. 【Todo】OSGi学习

    经常听到.见到OSGi这个名字.那么就单开一篇文章记录一下对OSGi的学习吧. 主要是做一些概念上面的学习.暂时不打算深入实践. 主要参考:http://www.osgi.com.cn/article ...

  2. OSGI学习总结

    最近的一项研究了解了一下OSGI技术,感觉OSGI尽管有一定的学习难度.可是终于掌握和推广之后将是一项对系统开发比較实用的技术.在此和大家分享一下自己的感悟. 1.什么是OSGI OSGI直译为&qu ...

  3. osgi学习

    Bundle可以被动态地安装.启动.停止和卸载.Bundle是服务(Service)和组件(Component)的载体.在OSGi中,每个Bundle都有自己独立于其他Bundle的ClassLoad ...

  4. OSGi 学习(二)

    上一篇说了很多虚的东西,现在说点别的. OSGi系统的独立环境下的项目结构以及启动脚本. 先说项目结构,基于equinox的OSGi容器的项目结构如下所示: bin中定义启动脚本,停止脚本之类的. c ...

  5. OSGi 学习(一)

    从基础开始,先来说说OSGi的基本理念. OSGi通过隔离底层classloader,强制应用在设计的时候就考虑模块化,并且基于白板模式来支持服务的注册与订阅. 在OSGi中,模块可以等价理解为bun ...

  6. OSGi 学习之路(4) - osgi的模块化 java在模块化的局限性

    底层代码可见性控制 Java提供了private,public,protected和package private(无修饰符)这四种访问控制级别,不过这仅仅提供了底层的OO数据封装特性.包这个概念确实 ...

  7. Liferay7 BPM门户开发之38: OSGi模块化Bndtools、Maven、Gradle开发构建入门

    前言 OSGi是目前动态模块系统的事实上的工业标准,它适用于任何需要模块化.面向服务.面向组件的应用程序.Eclipse如此庞大和复杂的插件体系,就是基于OSGi.Liferay也是基于OSGi.OS ...

  8. OSGI.NET 学习笔记--架构篇

    关于osgi.net ,想必大家也听说过,以下是自己在学习osgi.net 过程中整理出来的内容,供大家学习参与使用. 1.  UIOSP 开放工厂框架架构 开放工厂所有插件基于OSGi.NET面向服 ...

  9. OSGI.NET 学习笔记--应用篇

    关于osgi.net ,想必大家也听说过,以下是自己在学习osgi.net 过程中整理出来的内容,供大家学习参与使用. 1.  OSGI.NET 与UIOSP OSGi是Open Service Ga ...

随机推荐

  1. 激活函数之softmax介绍及C++实现

    下溢(underflow):当接近零的数被四舍五入为零时发生下溢.许多函数在其参数为零而不是一个很小的正数时才会表现出质的不同.例如,我们通常要避免被零除或避免取零的对数. 上溢(overflow): ...

  2. 使用stringstream类

    当需要格式化int类型为字符串时,可以使用这个类, 需要包含这个文件头: #include <sstream> 然后这样使用: //打开保存进度的RPG文件. std::stringstr ...

  3. Iphone 消息通知(APNS)的3种方式 -- C# 和 nodejs

    发送 APNS 使用 p12 文件(C#) public static bool PushWithP12(string apnToken, string message) { _log.DebugFo ...

  4. IOS开发 多线程编程 - NSOperation

    一.NSOperation 1.简介 NSOperation实例封装了需要执行的操作和执行操作所需的数据,并且能够以并发或非并发的方式执行这个操作. NSOperation本身是抽象基类,因此必须使用 ...

  5. zookeeper 学习命令

    ls /TianheSoft/nodesls /TianheSoft/nodes/localhost_2181-0000000000ls /TianheSoft/propsls /TianheSoft ...

  6. HOG特征简述

    ---恢复内容开始--- 参考文献:http://blog.csdn.net/zouxy09/article/details/7929348 分为几步: 1)cell:有n×m个像素组成,计算每个像素 ...

  7. MySQL基础(《MySQL必知必会》简单总结)

    使用MySQL # 选择数据库 USE database_name; # 显示数据库 SHOW DATABASES; # 显示当前数据库的表 SHOW TABLES; # 显示特定表有哪些列 SHOW ...

  8. 【剑指offer】09-2跳台阶,C++实现

    原创博文,转载请注明出处! # 本文是牛客网<剑指offer>刷题笔记 1.题目 # 一只青蛙一次可以跳1级台阶,也可以跳2级.求该青蛙跳n级的台阶总共有多少种跳法. 2.思路 # 跳0级 ...

  9. Unity是什么?

    Unity是patterns & practices团队开发的一个轻量级.可扩展的依赖注入容器,具有如下的特性: 1. 它提供了创建(或者装配)对象实例的机制,而这些对象实例可能还包含了其它被 ...

  10. phpcms后台主菜单不显示

    phpcms\modules\admin\templates\main.tpl.php 注释掉既可