在之前一篇我写的文章OSGi 探秘系列 (1)- 概述什么是OSGi框架中,OSGi对Java平台的类加载机制的一个重要改进就是支持包级别的类导入和导出,而不仅仅在jar级别。本文将详细介绍OSGi这一改进机制,并介绍在OSGi中如何定义和解析Bundle之间的依赖关系。
在Java以及很多其它面向对象编程语言中,类之间的依赖关系都是在code中隐含的,当依赖关系不满足的时候,在运行时会产生ClassNotFoundException或者NoClassDefFoundError之类的异常或者错误。OSGi则改变了这一状况,类之间的依赖关系是显式声明的以及版本化的。显式声明指的是依赖关系是按照某种标准的方式写在文件里,任何人任何程序都可以访问和看懂,而不是写在代码里;版本化指的是依赖关系显示声明所依赖的类的版本。
我们先通过一个例子介绍如何在Bundle中Export和Import包。该例子实例了一个MailBox服务,包括了两个Bundle。
=============================== 第一个Bundle ===============================
包含了两个接口
// org.osgi.message.reader.api.MailBox.java
packageorg.osgi.message.reader.api;
public interfaceMailBox {
public static finalStringNAME_PROPERTY= "mailBoxName";
long[] getAllMessages()throwsMailBoxException;
long[] getMessagesSince(longid)voidmarkRead(booleanread,long[] ids)throwsMailBoxException;
Message [] getMessages(throwsMailBoxException;
}
// org.osgi.message.reader.api.Message.java
packageorg.osgi.message.reader.api;
importjava.io.InputStream;
public interfaceMessage {
longgetId();
String getSummary();
String getMIMEType();
InputStream getContent()throwsMessageReaderException;
}
Export-Package: org.osgi.message.reader.api;version=1.0.0
右击然后点击Make Bundle,可以看到生成了Bundle的jar文件mailBox_api.jar.
=============================== 第二个Bundle ===============================
该Bundle包含了对上面两个接口的实现类。
//org.osgi.message.reader.fixedmailBox.FixedMailBox.java
packageorg.osgi.message.reader.fixedmailBox;
importjava.util.ArrayList;
importjava.util.List;
importorg.osgi.message.reader.api.MailBox;
importorg.osgi.message.reader.api.MailBoxException;
importorg.osgi.message.reader.api.Message;
public classFixedMailBoximplementsMailBox {
protected finalList<Message> messages;
publicFixedMailBox() {
messages = new ArrayList<Message>(2);
messages.add(new StringMessage(0,"Hello","Welcome to OSGi"));
messages.add(new StringMessage(1,"Getting Started","Study OSGi in depth"));
}
// Omit the implementation of the inherited methods from MailBox.java
}
// org.osgi.message.reader.fixedmailBox.StringMessage.java
importjava.io.ByteArrayInputStream;
importjava.io.InputStream;
importorg.osgi.message.reader.api.Message;
importorg.osgi.message.reader.api.MessageReaderException;
publicclassStringMessageimplementsMessage {
private static finalString MIME_TYPE_TEXT = "text/plain";
privatelong id;
privateString summary;
privateString content;
publicStringMessage(longid,String summary,String content){
this.id = id;
this.summary = summary;
this.content = content;
}
// Omit the implementation of the inherited methods from Message.java
创建一个fixed_mailBox.bnd的文件,文件内容如下
Private-Package: org.osgi.message.reader.fixedmailBox;
生成了Bundle的jar文件mailBox_api.jar. 在这个jar文件中,MANIFEST 的文件内容如下。可以看到,BND编辑器自动解析了在实现类中import的java包,然后加在了Import-Package中。
Bnd-LastModified 1262573897968
Bundle-ManifestVersion 2
Bundle-Name fixed_mailBox
Bundle-SymbolicName fixed_mailBox
Bundle-Version 0
Created-By 1.6.0_13 (Sun Microsystems Inc.)
Import-Package org.osgi.message.reader.api
Manifest-Version 1.0
Private-Package org.osgi.message.reader.fixedmailBox
Tool Bnd-0.0.384
在上面的例子中,我们看到第一个Bundle所Export的包org.osgi.message.reader.api被第二个Bundle Import了,我们就说第二个Bundle对第一个Bundle存在依赖关系。
在OSGi R4中,引入了一个新的header叫做"required-Bundle",其实际效果等于Import该Bundle所有Export出来的包。我觉得这其实是一个非常有用的header。想象一下在一个有数以千计的Bundle的系统中,如果逐个指定包层次的依赖关系将会是怎样一个噩梦。另外值得注意的是,所有java.*的包都是默认被Import的,不需要再在MANIFEST文件中声明,但是javax.*的包则需要声明。
从上面我们可以看到OSGi如何通过显示声明的方式定义Bundle之间的依赖关系,下面我们介绍在依赖声明中如何指定版本化信息。在OSGi中,版本信息的格式是三个数字段加上一个字符串段,比如"1.2.3","1.1","1.0.2.beta1". 下面这个例子可以让大家了解如何基本使用版本信息。
Export-Package org.osgi.message.reader.api;version="1.0.0"
Bundle-Version 1.0.0
另外,版本也可以用范围的形式指定。假设版本值用变量v表示,(1.0,2.0)表明1.0<v<2.0,[1.1.2,1.2.1]表明1.1.2<= v<=1.2.1,以此类推。下面两个例子示范了如何在Import包以及Bundle中使用版本范围。
Import-Packageorg.apache.log4j.*; version = " [ 1 . 2,1 . 3 ) "
Require-BundlemailBox-api;bundle-version="[1.0.0,1,1.2]"
在这篇文章的最后,我再总结一下OSGi框架下的类加载机制。当Bundle被安装后,OSGi按照以下顺序进行类加载:
1) 从上层类加载器中加载java.*的包
2) 加载MANIFEST中的Import-Package所指定的包
3) 加载MANIFEST中的Require-Bundle所指定的Bundle
4) 加载该Bundle自身包含的类
5) 从Fragment bundle中加载类。
原文链接:https://www.f2er.com/javaschema/285694.html