1 什么是模块化

模块层是OSGi框架中最基础的一部分,其中Java的模块化特性在这一层得到了很好的实现。但是这种实现与Java本身现有的一些模块化特性又有明显的不同。

在OSGi中模块的定义可以参考下图:

也就是说一个模块(module)定义了一个逻辑边界,这种模块本身精确的控制了哪些类是完全被封装起来的,而哪些类需要暴露出来作为外部使用。这样我们就可以轻松的将实现屏蔽在模块的内部,而将公共API暴露在外部。

2 为什么需要模块化

2.1 OSGi中模块化与面向对象的联系与区别

在用Java编写面向对象程序的时候,一个了解面向对象概念的人是不会把所有功能都塞到同一个类里面去的,面向对象让你从问题域中发现多个事物,并且每个事物负责不同的功能,尽量做到高内聚和低耦合。在这里,我们可以说面向对象的模块化粒度是在“类”这个级别上。 
而OSGi的模块化,则是通过为JAR包添加metadata来定义哪些类应该暴露哪些类又隐藏在包中,其控制可见性的粒度是在bundle(JAR包)这一层面上的。

所以,它们所带来的能力都是通过控制可见性和可用性来保证高内聚和低耦合的,但是粒度不同,一个是对象层面上的,一个是模块层面上的。

2.2 Java在模块化方面的局限性

2.2.1 底层代码可见性控制

Java提供了private,public,protected和package private(无修饰符)这四种访问控制级别,不过这仅仅提供了底层的OO数据封装特性。包这个概念确实是起到了分割代码的作用,但是如果包中的代码需要对包外可见,那么必须设置为public(或者protected,如果是使用了继承的话)。 这样的话就可能出现一个问题:

首先大家看看下面的例子,其中有三个java文件: 
org.serc.helloworld.Hello.java:定义了一个接口

package org.serc.helloworld;

public interface Hello{

  void sayHello();

}

org.serc.helloworld.impl.HelloImpl.java:实现了Hello接口

package org.serc.helloworld.impl;

import org.serc.helloworld.Hello;

public class HelloImpl implements Hello {

  final String helloString;

  public HelloImpl (String helloString) { 

    this.helloString = helloString;

  }

  public void sayHello(){

  System.out.println(this.helloString);

  }

}

org.serc.helloworld.main.Main.java
package org.serc.helloworld.main;

import org.serc.helloworld.Hello;

import org.serc.helloworld.HelloImpl;

public class Main { 

  final String helloString;

  public static void main(String[] args){

  Hello hello =newHelloImpl(“Hello,SERC!”);
   hello.sayHello();}

}

这三个文件分别在不同的包中。按理说,HelloImpl这个实现细节是不应该暴露给其他包的,但是从Main.java的main方法中我们可以明显的看出,为了创建Hello 的实例,我们不得不引入HelloImpl类,但是HelloImpl作为接口的实现细节,是不应该暴露给使用者的,这违反了封装的原则,显然不太好。

但是,如果我们不想让HelloImpl暴露出来的话,就需要做额外的工作来保证“既隐藏了实现细节,又能简单的创建一个实现了Hello接口的实例”。达到这一目的的方法不止一种(比如工厂模式),有些至今也是很常用的,但是这增加了与应用本身功能无关的多余工作,想想如果你每次想开发一个应用都要为了达到上述目的而做出多余工作,不得不说是有点繁琐的,所以这可以说是Java的一大局限。

2.2.2 classpath的局限

我们在classpath中加入jar包的时候,只是简单的给出文件路径,而这个jar包的版本和一致性,它所依赖的jar包是什么,我们都无法在classpath中明确的设置或是从classpath中看出这些属性。 
并且classpath中的jar包是按序加载的,例如:

classpath=c:\servlet2.2\servlet.jar;c:\servlet2.3\servlet.jar,

那么在实际应用的过程中,Java让你使用的是servlet2.2,而不是servlet2.3。这种情况下我们还能看出来使用的是哪个版本,如果在大型系统中大家分开开发的时候各用各的servlet包,并且版本号不一样,那么在最后将开发结果合并的时候,到时候用的是哪个版本的servlet包就很难搞清楚了,也就说不可控性是比较强的。 
即使classpath能注意到版本的问题,也没法精确指出依赖。试着回想你在设置classpath的过程中出现过情况:在你以为classpath已经设置完毕以后,你尝试启动程序,结果虚拟机抛出异常告诉你缺包,然后你再加上你觉得缺少的那些包,然后再启动程序,如此反复直到虚拟机不运行到缺包异常为止。

2.3 OSGi对这些局限性的改善

对于上一小节提到的Java的局限,在OSGi中都得到了很好的解决。

  • 包的可见性:OSGi通过引入包的可见性机制,能够完全控制一个包中的代码对哪些模块可见,而不仅仅局限于无差别的可见性,从而完善了Java的代码访问控制机制。
  • 包的版本: OSGi通过为包增加版本信息,可以精确控制代码的依赖,保证代码的版本一致性,弥补了classpath的缺点。

3 OSGi模块层基础

3.1 Bundle的概念

这是模块层最核心的概念,也是模块(module)这个概念在OSGi中的具象表现。接下来你将会在OSGi的世界中创建和使用数不胜数的bundle。 
什么是bundle?——bundle是以jar包形式存在的一个模块化物理单元,里面包含了代码,资源文件和元数据(metadata),并且jar包的物理边界也同时是运行时逻辑模块的封装边界。

一个更为直观的说明:在标准的jar包的manifest文件中添加一些bundle的模块化特征(就是前面提到的metadata)后,这个jar包就变成了一个bundle。 
那么有了上面的描述,你大概也能想到,bundle和普通jar包最大的区别就在于元数据。

3.2 使用元数据来定义bundle

Bundle元数据的目的在于准确描述模块化相关的bundle特征,这样才能让OSGi框架恰当的对bundle进行各种处理工作(比如依赖解析,强制封装等),这些元数据主要有这三个部分:

  • 可读的信息(可选):帮助更好地理解和使用bundle
  • bundle的标识符(必须):唯一的标识一个bundle
  • 代码可见性(必须):定义内部与外部代码

3.2.1 可读的信息

这些内容可以帮助人们直观的了解这个bundle是做什么的,从哪里来。OSGi标准定义了几个元数据条目来达到这个目的,但是所有的条目都不是必须的,并且也不对模块化特性产生任何的影响,OSGi框架会完全无视这些内容。 
下面就是一个这类信息的例子:

Bundle-Name: SERC HelloworldBundle-Vendor: GR, SERC
Bundle-DocURL: http://elevenframework.orgBundle-Category: example
Bundle-Copyright: SERC  

3.2.2 bundle的标识符

在上个小节提到的可读信息中,有些似乎也能用来唯一标识一个bundle,比如Bundle-Name,但是事实上,他并没有被用来标识一个bundle。早期的OSGi标准中并没有提供标识一个已知bundle的方法,直到OSGi R4标准“唯一bundle标识符”这个东西才被提出来。为了向后兼容,Bundle-Name就不能用来作为标识符了,否则就会增加维护向后兼容的工作,所以新的manifest属性就诞生了:Bundle-SymbolicName

Bundle-SymbolicName: org.serc.helloworld 
两者相比,Bundle-Name是给用户读的,而Bundle-SymbolicName是给OSGi框架读的,让OSGi框架能够唯一标识一个bundle。

只用一个Bundle-SymbolicName肯定是可以唯一标识一个bundle了,但是随着时间的推移,你的bundle可能会有新版本,这时候加入版本属性会让你的bundle的信息更加准确。

Bundle-Name: SERC HelloworldBundle-Vendor: GR, SERC
Bundle-DocURL: http://elevenframework.orgBundle-Category: example
Bundle-Copyright: SERC  

3.2.3 代码可见性

在JavaSE中的jar包如果放在了classpath里面,那么它对这个classpath下的所有程序都是可见的,并且这种可见性不能改变,而OSGi标准定义了如下的属性用于描述代码的可见性:

  • Bundle-ClassPath—它定义了形成这个bundle的所有代码所在的位置,和Java的classath的概念相近,不同点在于,Java中的classpath是定义的jar包的位置,而这个属性描述的是bundle内部的类在bundle中的路径。有例子如下:

    Bundle-ClassPath:.,other-classes/,embedded.jar  
  • Export-Package—显式暴露需要和其他bundle共享的代码,每个包之间用逗号分隔,每个包可用修饰词来修饰包的其他特征。

    Export-Package: org.serc.hellworld; vendor=”SERC”,
    org.serc.hellworld.impl; vendor=”GouRui”
  • Import-Package—定义该bundle所依赖的外部代码,其格式和Export-Package相同,并且也可以使用修饰词来修饰包。不过这里的修饰词是用来限制所依赖包的范围的,像是一个过滤器,而不像Export-Package中用来声明包的特征。例如如下语句:

    Import-Package: org.serc.helloworld; vendor=”SERC”

OSGi-入门篇之模块层(02)的更多相关文章

  1. 转:OSGi 入门篇:模块层

    OSGi 入门篇:模块层 1 什么是模块化 模块层是OSGi框架中最基础的一部分,其中Java的模块化特性在这一层得到了很好的实现.但是这种实现与Java本身现有的一些模块化特性又有明显的不同. 本文 ...

  2. 转:OSGi 入门篇:生命周期层

    OSGi 入门篇:生命周期层 前言 生命周期层在OSGi框架中属于模块层上面的一层,它的运作是建立在模块层的功能之上的.生命周期层一个主要的功能就是让你能够从外部管理应用或者建立能够自我管理的应用(或 ...

  3. Farseer.net轻量级开源框架 入门篇:逻辑层的选择

    导航 目   录:Farseer.net轻量级开源框架 目录 上一篇:Farseer.net轻量级开源框架 入门篇: 入门篇:增.删.改.查操作演示 下一篇:Farseer.net轻量级开源框架 入门 ...

  4. 11-(基础入门篇)WiFi模块开发,下载运行第一个程序

    https://www.cnblogs.com/yangfengwu/p/9954840.html 第一就是重新刷一下固件,咱们的固件保持一致,有问题好处理 先刷空固件 我用的 所以刷8Mbit的 给 ...

  5. IT咨询顾问:一次吐血的项目救火 java或判断优化小技巧 asp.net core Session的测试使用心得 【.NET架构】BIM软件架构02:Web管控平台后台架构 NetCore入门篇:(十一)NetCore项目读取配置文件appsettings.json 使用LINQ生成Where的SQL语句 js_jquery_创建cookie有效期问题_时区问题

    IT咨询顾问:一次吐血的项目救火   年后的一个合作公司上线了一个子业务系统,对接公司内部的单点系统.我收到该公司的技术咨询:项目启动后没有规律的突然无法登录了,重新启动后,登录一断时间后又无法重新登 ...

  6. 每天记录一点:NetCore获得配置文件 appsettings.json vue-router页面传值及接收值 详解webpack + vue + node 打造单页面(入门篇) 30分钟手把手教你学webpack实战 vue.js+webpack模块管理及组件开发

    每天记录一点:NetCore获得配置文件 appsettings.json   用NetCore做项目如果用EF  ORM在网上有很多的配置连接字符串,读取以及使用方法 由于很多朋友用的其他ORM如S ...

  7. Farseer.net轻量级开源框架 入门篇:分类逻辑层

    导航 目   录:Farseer.net轻量级开源框架 目录 上一篇:Farseer.net轻量级开源框架 入门篇: 缓存逻辑层 下一篇:Farseer.net轻量级开源框架 入门篇: 添加数据详解 ...

  8. Python入门篇-基础数据类型之整型(int),字符串(str),字节(bytes),列表(list)和切片(slice)

    Python入门篇-基础数据类型之整型(int),字符串(str),字节(bytes),列表(list)和切片(slice) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.Py ...

  9. spring boot(一):入门篇

    构建微服务:Spring boot 入门篇 什么是spring boot Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框 ...

随机推荐

  1. http基础知识总结

    前车之鉴,后车之师. 站在各位前辈的肩膀上学习到很多知识,这里仅做记录,供自己使用 关于HTTP 我们想要打开一个网站,首先是需要往浏览器地址的URL输入框架中输入网址.当敲下回车后,通过http协议 ...

  2. textarea 空格的问题

    如果textarea开始标签和结束标签中间有空格,则textarea默认有空格. <textarea name=" > </textarea> 效果图 所以,如果想去 ...

  3. extjs6中grid里放置图片

    1.加黑体的是实现代码,在view中操作 /** * Created by Wwei on 2017/7/1. */ Ext.define('Admin.view.userpanoram.UserPa ...

  4. RPC服务不可用总结

    A简单方法: 通过"控制面板/管理工具/服务",检查一下RPC的Remote Procedure Call (RPC)和Remote Procedure Call (RPC) Lo ...

  5. 【.net 深呼吸】实时获取计算结果

    上次老周介绍了在 UWP 应用中通过 x:Bind 标记来绑定到方法,以实现实时获取计算结果.今天,咱们来耍耍WPF上面的实现方法. 虽然,WPF 没有 x:Bind 标记(暂时没有,以后不好说),但 ...

  6. Partition函数

    快排中核心的方法应该算是Partition函数了,它的作用就是将整个数组分成小于基准值的左边,和大于基准值的右边. 普通的Partition函数是这样的: public static int part ...

  7. Spring Boot-------项目搭建及注解

    Spring Boot Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置,从而使开发人员不再需 ...

  8. Maven(二)Maven项目的创建(命令、myeclipse)及生命周期

    上一篇给大家介绍了Maven的概念和仓库的一些信息,接下来给大家分享一下使用命令和MyEclipse创建Maven项目 一.使用命令管理Maven项目 1.1.创建Maven java项目 1)创建一 ...

  9. 在windows中,使用SSH登录VMware ubuntu linux虚拟机

    测试环境 主机:window7 sp1 64位 专业版 虚拟机:VMware workstation 12 player 虚拟机操作系统: ubuntu 16.4 目标:在ubuntu下运行SSH服务 ...

  10. !Web云笔记--HTML基础

    Web自学笔记第一阶段笔记综合汇总 参考资料:<Head First HTML&CSS>(中文第二版)(美国)弗里昂ISBN:9787508356464 中国电力出版社 全部阶段: ...