本文是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框架将按照以下顺序搜索该类。
- 如果包含该类的报名以java.开头,当前类加载器的父类加载器会去搜索这个类。如果找到这个类,就使用它,如果没有找到,搜索以异常结束。
- 如果这个类是bundle的导入包中的类,OSGi框架从导出bundle中搜索这个类。如果找到了,就使用它,如果没有找到,该搜索以异常结束。
- 从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处于启动状态时,才能够进入启动状态,一个独立组件的生命周期基于其自身的依赖关系和制约因素。
相关推荐
通过Spring OSGi,开发者可以利用OSGi的动态性来更灵活地管理应用组件,但同时也要面对OSGi学习曲线陡峭、调试困难等问题。不过,随着Spring OSGi的不断成熟,这些问题正在逐步得到解决。 8. **未来发展** 虽然...
总结来说,"carrot-osgi-anno-scr-make"是一个实用的工具,它简化了OSGi DS组件的开发过程,通过注解驱动的方式自动生成组件描述符。对于那些涉及OSGi服务组件开发的项目,它无疑是一个值得尝试的利器。同时,该项目...
根据文档描述,作者在学习OSGI时遇到了一些基础性的问题,特别是关于如何正确配置并运行一个基本的OSGI环境。文档中提到了使用Bluedavy进行OSGI实践,但在执行`run.bat`脚本时遇到了问题。 **问题分析:** 1. **...
总结来说,Spring OSGi 1.2.0是Spring框架与OSGi技术的结合,它提供了一种在模块化环境中使用Spring的途径,增强了应用的灵活性和可维护性。通过这个压缩包,开发者可以获取所需的所有资源来开始在OSGi容器中开发和...
总结来说,"osgi-in-action-20090225"压缩包提供的源代码是对《OSGi实战》理论知识的实践补充,读者可以通过它们深入理解OSGi的机制,提升Java模块化开发能力。无论是初学者还是经验丰富的开发者,都能从中获益,...
总结起来,Flex OSGi实现,特别是Potomac框架,提供了一种灵活且强大的方式来管理和传递数据。它利用OSGi的模块化和服务注册机制,使得Flex组件可以方便地进行数据交互,提高了应用程序的可扩展性和可维护性。通过...
osgi学习之个人总结,这是个人学习OSGI时候的总结,这里有个人的理解,对于初学者有所帮助,可以少走弯路
### OSGI学习手册及实践知识点总结 #### 1. OSGI简介与背景 - **OSGI**(Open Service Gateway Initiative)是一种用于构建模块化应用程序和服务的框架,旨在提高软件系统的灵活性、可扩展性和可维护性。它最初是...
总结来说,OSGi提供了一个强大的模块化系统,通过服务导向的架构,使Java应用更加灵活和可维护。开发者通过掌握OSGi,能够构建更高效、可扩展的系统,同时也能更好地应对复杂的应用场景和持续变化的需求。如果你计划...
### 总结 通过上述分析可以看出,OSGI为Java应用程序提供了强大的模块化支持,而SpringDM进一步增强了这种能力。在实际开发过程中,需要注意处理好版本兼容性问题以及依赖管理,以确保项目的顺利进行。此外,合理...
总结,org.eclipse.osgi作为OSGi的实现,是理解模块化开发和动态服务的关键。通过深入研究其3.7.0.v20110613版本及源码,开发者不仅能掌握OSGi的基本概念,还能在实践中提升软件设计和架构的能力。
### OSGI实战知识点概述 #### 一、序言与背景介绍 ...综上所述,《OSGI实战》不仅涵盖了OSGI的基本概念和技术细节,还提供了丰富的实战案例和深入的技术解析,为读者全面了解和掌握OSGI提供了宝贵的学习资料。
### OSGi实战进阶篇知识点总结 #### 一、序言与背景 - **序言**:作者从个人经历出发,表达了对于插件体系结构的兴趣,并提到了早期接触的一些工具,如Ant和Maven,这表明作者对于构建工具及模块化开发有着深厚的...
需要注意的是,尽管Spring-DM带来了诸多便利,但在引入新特性的同时也可能带来额外的学习成本和调试难度。 #### 二、Spring OSGi 实现机制概述 1. **每个Bundle都有一个ApplicationContext**: 每个OSGi Bundle都...
在本篇OSGi学习笔记中,我们将深入探讨OSGi(Open Service Gateway Initiative)这一模块化系统,特别是关于服务方面的知识。OSGi是一个Java平台上的动态模块化系统,它允许开发者创建可热部署、互相依赖的模块,...
OSGi(Open Services Gateway Initiative)是一种Java模块化...总结,OSGi是一个强大的模块化系统,对于构建可维护、可扩展的Java应用程序具有显著优势。理解和掌握OSGi,可以帮助开发者构建更加健壮、灵活的软件系统。
- 最佳实践:分享开发者在OSGi开发过程中总结的经验和技巧,帮助避免常见陷阱。 3. **OSGI实战.pdf**: 这本书可能专注于通过实际项目展示OSGi的应用: - 开发工具:介绍支持OSGi的IDE(如Eclipse)和构建工具...
- **6.9 注意事项**:总结在OSGI开发过程中常见的陷阱和需要注意的细节。 #### 七、深入OSGI - **7.1 关于OSGI**:提供了更深层次的理解,包括其历史背景和发展趋势。 - **7.2 OSGI R4规范** - **7.2.1 Core ...
总结来说,"OSGI进阶源码和中文文档"是一份宝贵的资源,适合已经对OSGI有一定基础的开发者,希望通过实战和深入学习进一步提升技能。通过这份资料,你可以掌握如何构建、管理和优化基于OSGI的模块化Java应用,从而在...
7. **OSGi最佳实践**:最后,文档总结了一系列OSGi的最佳实践,包括将接口和实现分离为不同的 bundles、保持系统的动态性、搭建公司级的 bundle repository、创建共享 library bundle、最小化依赖以及避免启动顺序...