OSGi 探秘系列 (4)- OSGi Bundle之依赖关系

前端之家收集整理的这篇文章主要介绍了OSGi 探秘系列 (4)- OSGi Bundle之依赖关系前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

在之前一篇我写的文章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;

}

创建一个mailBox_api.bnd的文件文件内容如下

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中加载类。

猜你在找的设计模式相关文章