分类: Java
2012-02-06 14:16:50
1. 模块层
1.1简介
模块层定义了在OSGI框架中是如保按照模块的思想去进行开发的。很多基于java的项目,如JBoss、NetBeans,常常借助于专用的类加载器来创建用户模块层,以实现打包、部署和对Java应用和组件检验。OSGi框架提供了对java模型化的一般和标准的解决方案。在OSGI中模块就是命名为 Bundle,一个Bundle由java的类和其他资源组成,Bundle可以通过导入(importer)及导出(exporter) Bundle之间共享Java包。Bundle其实就是一个jar,不同的是jar的META-INF目录下的MANIFEST.MF,这个文件存储了 Bundle的元数据信息。下面是MANIFEST.MF中的元数据信息
Bundle-Activator: com.acme.fw.Activator描述指出启动和停止bundle的类名称。
Bundle-Category: osgi, test, nursery描述用逗号分隔的分类名称。
Bundle-Classpath: /jar/http.jar,定义用逗号分隔的路径,包含的内容有JAR文件和包含类和资源的目录(bundle内 部)。点号(‘.’)代表JAR文件的根目录,同时也是默认的。参考bundle类路径一节。
Bundle-ContactAddress: 2400 Oswego Road, Austin, TX 74563标识bundle发行者的联系信息。
Bundle-Copyright: OSGi (c) 2002描述bundle的版权信息。
Bundle-Description: Network Firewall对bundle的简短描述信息。
Bundle-DocURL: http:/文档的链接地址。
Bundle-Localization: OSGI-INF/l10n/bundle描述bundle的本地文件地址,默认值是OSGI-INF/l10n/bundle。
Bundle-ManifestVersion: 2定义了bundle遵循本规范的那种规则。默认值为1,表示第三个版本的bundle,2表示第四个版本及其后发布的版本。也可以为OSGi新发布的版本定义更高的数字。
Bundle-Name: Firewall定义了一个具有可读性的名字来标识bundle。应该是一个简短易读没有空格的名字。
Bundle-NativeCode: /lib/http.DLL; osname = QNX; osversion = 3.1对bundle中包含的本地代码库的规范。
Bundle-RequiredExecutionEnvironment: CDC-1.0/Foundation-1.0描述在服务平台上必须出现的了可执行环境的,用逗号分割。
Bundle-SymbolicName: com.acme.daffy提供了bundle的一个全局的惟一的标志符。名称应该是基于反域名解析的。
Bundle-UpdateLocation: 描述bundle的更新地址。如果bundle需要更新,则使用这个地址进行更新。
Bundle-Vendor: OSGi Alliance描述bundle的发行者信息。
Bundle-Version: 1.1描述bundle的版本信息。默认值为0.0.0
DynamicImport-Package: com.acme.plugin.*包含了一个逗号分隔的动态导入包清单。参考动态导入包。
Export-Package: org.osgi.util.tracker;version=1.3描述导出包声明
Fragment-Host: org.eclipse.swt; bundle-version="[3.0.0,4.0.0)"描述本片断中的主bundle
Import-Package: org.osgi.util.tracker,org.osgi.service.io;version=1.4声明bundle导入的包。
Require-Bundle: com.acme.chess指定bundle中需要其他bundle导出的内容。
1.2 类的加载机制
许多bundle可以共享虚拟机(VM)。在VM内部,bundle可以相互隐藏包和类,也可以和其他bundle共享包。
隔离和共享包关键是由java的类加载器来实现,类加载器通过仔细定义的规则从bundle空间的一个子集中加载类。每一个bundle只会有一个单独的类加载器,类加载器形成了一个类加载的代理网络结构,如下所示:
在OSGI中,ClassLoader可以通过下面几个区域来加载类和资源:
启动类路径:启动类路径中有一个java.*的包以及它实现的包。
框架类路径:在框架中通常有一个单独的类加载器,加载框架实现的类和关键的服务接口类。
Bundle类空间:bundle的类空间由和bundle相关的JAR文件组成,以及其他和bundle紧密相关的JAR文件
类空间是通过一个给定的Bundle的的ClassLoader可以获取的类。对Bundle而言,它的ClassLoader能加载的类包括:父类加载器加载的类(通常是来自启动类路径的java.*包中的),当前Bundle导入的包,必须的Bundle, Bundle类路径,附加的其它的Bundle。
在OSGI规范中有张经典的图可说明Bundle的class的加载机制的:
如图所示,OSGI框架在加载Bundle中的类时按照这样的步骤进行:
如需要加载的为java.*的类,则直接委派给Parent Classloader,如在parent Classloader中找到了相应的类,则直接返回,如未找到,则抛出NoClassDefFoundException。
如加载的不是java.*的类,则进入这一步。判断加载的类是否属于boot delegation中配置的范围,如不属于则进入下一步,如属于则继续委派给Parent Classloader,如在Parent Classloader中找到则直接返回,如未找到,则进入下一步。可在配置文件中编写org.osgi.framework.bootdelegation的属性来决定boot delegation的范围,示例:
org.osgi.framework.bootdelegation=sun.*,com.sun.*
如属于Bundle Import package中的类,则交给export package的Bundle的classloader进行加载,如加载失败,则直接抛出NoClassDefFoundException,如加载成功则直接返回。这步就解释了之前在注意事项中所写的需要注意的包的问题。
如不属于Bundle Import package中的类,则搜索是否属于Require Bundles中export的package的类,如属于则交由export package的Bundle的Classloader进行加载,如加载成功则直接返回,如加载失败则进入下一步。
在Bundle classpath(就是在Bundle-Classpath所配置的路径)中搜索需要加载的类,如加载成功,则直接返回,如加载失败则继续进入下一步。
搜索Fragment Bundle(还记得配置的Fragment-Host吧)的classpath,如加载成功,则直接返回,如加载失败则继续进入下一步。
判断是否属于export的package,如属于则直接抛出NoClassDefFoundException,如不属于则进入下一步。
判断是否属于DynamicImport的package,如不属于则直接抛出NoClassDefFoundException,如属于则使用export package的Bundle的ClassLoader进行加载,如加载成功则直接返回,如加载失败则抛出NoClassDefFoundException。
对于Bundle中的资源文件,可使用bundle.getResource、bundle.getEntry或bundle.findEntries来获取,返回的为一个可被转变为java.net.URL的对象。通过URL就可加载到相应的资源文件,如果要获取到其他Bundle的资源文件则需通过设置Require-Bundle的方式才可获取,Require-Bundle也可视为实现资源文件共享的一种方式,不过Require-Bundle并不是被推崇的一种方式,在OSGI规范中,认为Require-Bundle会造成split packages。