精品人人都会OSGI实例讲解OSGI开发文档格式.docx
《精品人人都会OSGI实例讲解OSGI开发文档格式.docx》由会员分享,可在线阅读,更多相关《精品人人都会OSGI实例讲解OSGI开发文档格式.docx(17页珍藏版)》请在冰豆网上搜索。
![精品人人都会OSGI实例讲解OSGI开发文档格式.docx](https://file1.bdocx.com/fileroot1/2022-11/29/a5222a71-8726-42c0-98aa-d5a4d881042a/a5222a71-8726-42c0-98aa-d5a4d881042a1.gif)
从企业应用开发者的角度看,OSGI容器侵入性非常小,你可以方便地将其嵌入一个企业应用。
举个例子来说,假设你在开发一个复杂的web应用。
你希望将这个应用分解成多个功能模块。
一个View层模块,一个Model层模块,一个DAO模块。
使用嵌入式OSGI容器来跨依赖地管理这些模块可以让你随时更新你的DAO模块却不需要重启你的服务器。
只要你的应用完全符合OSGI规范,它就可以在所有符合OSGI规范的容器内运行。
现在,有三种流行的开源OSGI容器:
1.Equinox是OSGIServicePlatformRelease4的一个实现。
是Eclipse模块化运行时的核心。
2.Knopflerfish另一个选择。
3.ApacheFelix是Apache软件基金会赞助的一个OSGI容器
在这篇文章里我们使用Equinox作为我们的OSGI容器。
尝试开发一个HelloWorldbundle
在OSGI的领域,发布的软件是以bundle的形式出现。
bundle由javaclass类和资源文件组成,向设备所有者提供功能,同时可以为其他的bundles提供服务。
Eclipse对开发bundles提供了强大的支持。
Eclipse不仅仅提供创建bundles的功能,它还集成了Equinox这个OSGI容器,你可以在其上开发和调试OSGI组件。
其实所有的Eclipse插件都是使用Eclipse规范代码写的OSGIbundle。
接下来,你将可以学到如何使用EclipseIDE开发一个Helloworldosgibundle。
开始开发bundle
我们一步步的开始:
1.启动Eclipse,依次点File-->
New-->
Project。
2.选择Plug-inProject,next。
3.输入ProjectName项目名称,比如com.howard.sample.HelloWorld,TargetPlatform(目标平台)里的anOSGIframework,选择standard。
4.剩下的保持默认,next。
5.下个对话框也默认,next。
6.然后选择HelloOSGIBundle作为模版。
Finish。
Eclipse会飞快的为你创建Helloworldbundle的模版代码。
主要包含两个文件:
Activator.java和MANIFEST.MF。
Activator.java的代码如下所示:
Java代码
1.import
org.osgi.framework.BundleActivator;
2.import
org.osgi.framework.BundleContext;
3.public
class
Activator
implements
BundleActivator
{
4.
public
void
start(BundleContext
context)
throws
Exception
5.
System.out.println("
Hello
world"
);
6.
}
7.
stop(BundleContext
8.
Goodbye
World"
9.
10.}
如果你的bundle在启动和关闭的时候需要被通知,你可以考虑实现BundleActivator接口。
以下是定义Activator的一些注意点:
1.你的Activator类需要一个公有的无参数构造函数。
OSGI框架会通过类反射的方式来实例化一个Activator类。
2.容器启动bundle过程中负责调用你的Activator类的start方法。
bundle可以在此初始化资源比如说初始化数据库连接。
start方法需要一个参数,BundleContext对象。
这个对象允许bundles以取得OSGI容器相关信息的方式和框架交互。
如果某一个bundle有异常抛出,容器将对该bundle标记为stopped并不将其纳入service列表。
3.容器关闭的时候会调用你的Activator类方法stop(),你可以利用这个机会做一些清理的操作。
MANIFEST.MF
这个文件是你的bundle的部署描述文件。
格式和Jar里的MANIFEST.MF是一样的。
包含的不少名值对,就像如下:
Xml代码
1.Manifest-Version:
1.0
2.Bundle-ManifestVersion:
2
3.Bundle-Name:
HelloWorld
Plug-in
4.Bundle-SymbolicName:
com.howard.sample.HelloWorld
5.Bundle-Version:
1.0.0
6.Bundle-Activator:
com.howard.sample.helloworld.Activator
7.Bundle-Vendor:
HOWARD
8.Bundle-RequiredExecutionEnvironment:
JavaSE-1.6
9.Import-Package:
org.osgi.framework;
version="
1.3.0"
分别来看下:
Bundle-ManifestVersion
数值为2意味着本bundle支持OSGI规范第四版;
如果是1那就是支持OSGI规范第三版。
Bundle-Name
给bundle定义一个短名,方便人员阅读
Bundle-SymbolicName
给bundle定义一个唯一的非局部名。
方便分辨。
Bundle-Activator
声明在start和stop事件发生时会被通知的监听类的名字。
Import-Package
定义bundle的导入包。
HelloWorldbundle完成了,接下来我们运行一下。
执行bundle
1.点击Run-->
RunConfiguration
2.在左边的OSGIFramework选项里右键new,创建一个新的OSGIRunConfiguration
3.名字随便取好了,我们取个OSGihelloworld。
4.你会注意到中间的窗口里Workspace项目里有一子项com.howard.sample.HelloWorld,将其勾选上,其他的不用管。
这时的状态应该如下图。
5.点击Run按钮。
在控制台你应该可以看见点东西了。
那是叫做OSGI控制台的东东。
与子相伴,还有一个"
Helloworld"
。
OSGI控制台
OSGI控制台是一个OSGI容器的命令行界面。
你可以利用它做些诸如启动,关闭,安装bundles,更新和删除bundles等操作。
现在,点击OSGI控制台所在的位置,回车,你就会发现可以输入命令了。
这时的OSGI控制台应该如下图:
下面列出一些常用的OSGI命令,你可以试着和OSGI容器交互。
ss显示已安装的bundles的状态信息,信息包括bundleID,短名,状态等等。
start启动一个bundle
stop
关闭一个bundle
update
载入一个新的JAR文件更新一个bundle
install
安装一个新的bundle到容器中
uninstall
卸载一个已在容器中的bundle
依赖管理
OSGI规范允许你把你的应用分解成多个模块然后管理各个模块间的依赖关系。
这需要通过bundlescope来完成。
默认情况下,一个bundle内的class对其他bundle来说是不可见的。
那么,如果要让一个bundle访问另一个bundle里的class要怎么做?
解决的方案就是从源bundle导出包,然后在目标bundle里导入。
接下来我们对此做一个例子。
首先,我们需要先创建一个com.howard.sample.HelloServicebundle,我们将通过它导出一个包。
然后,我们在com.howard.sample.HelloWorld这个bundle里导入包。
导出包
1、创建名为com.howard.sample.HelloService的bundle,创建步骤和前面一样。
2、在这个bundle内,添加一个com.howard.sample.service.HelloService.java接口,代码如下:
1.public
interface
HelloService
2.
String
sayHello();
3.}
3、创建一个com.howard.sample.service.impl.HelloServiceImpl.java类实现刚才的接口:
HelloServiceImpl
HelloService{
sayHello()
3.
Inside
HelloServiceImple.sayHello()"
return
"
Say
Hello"
;
6.}
4、打开MANIFEST.MF,选择Runtime标签项,在ExportedPackages选项栏,点击Add并且选择com.howard.sample.service这个包。
然后MANIFEST.MF的代码应该如下:
com.howard.sample.HelloService
com.howard.sample.helloservice.Activator
10.Export-Package:
com.howard.sample.service
你可以看到,MANIFEST.MF文件和刚才的HelloWorld的那份很类似。
唯一的区别就是这个多了Export-Package这个标记,对应的值就是我们刚才选择的com.howard.sample.service。
Export-Package标记告诉OSGI容器在com.howard.sample.service包内的classes可以被外部访问。
注意,我们仅仅暴露了HelloService接口,而不是直接暴露HelloServiceImpl实现。
导入包
接下来我们要更新原来的HelloWorldbundle以导入com.howard.sample.service包。
步骤如下:
1、进入HelloWorldbundle,打开MANIFEST.MF,进入Dependencies标签页,在ImportedPackages里添加com.howard.sample.service。
MANIFEST.MF文件应该如下所示:
com.howard.sample.service,
10.
没错,Import-package标记的值也就是导入的包名之间是用逗号隔开的。
在这里导入了两个包om.howard.sample.service和org.osgi.framework。
后者是使用Activator类时必须导入的包。
2、接下来,打开HelloWorld项目下的Activator.java文件,这时候你会发现可以使用HelloService这个接口了。
但还是不能使用HelloServiceImpl实现类。
Eclipse会告诉你:
Accessrestriction(立入禁止)。
Class级别可见域
为什么OSGI容器可以做到让jar包中的一些classes可见而另一些又不可见呢。
答案其实就是OSGI容器自定义了javaclassloader来有选择的加载类。
OSGI容器为每一个bundle都创建了不同的classloader。
因此,bundle可以访问的classes包括
∙Bootclasspath:
所有的java基础类。
∙Frameworkclasspath:
OSGI框架级别的classloader加载的类
∙Bundleclasspath:
Bundle本身引用的关系紧密的JAR的路径
∙Importedpackages:
就是在MANIFEST.MF里声明的导入包,一旦声明,在bundle内就可见了。
bundle级别的可见域允许你可以随时放心的更改HelloServiceImpl实现类而不需要去担心依赖关系会被破坏。
OSGI服务
OSGI框架是实现SOA的绝佳土壤。
通过它可以实现bundles暴露服务接口给其他bundles消费而不需要让细节暴露。
消费bundles甚至可以完全不知道提供服务的bundles。
凭着可以良好的隐藏具体实现的能力,OSGI当之无愧是SOA的一种较完美的实现方案。
OSGI中,提供服务的bundle在OSGI容器上将一个POJO注册成一个service。
消费者bundle请求OSGI容器中基于某个特殊接口的注册service。
一旦找到,消费者bundle就会绑定它,然后就可以调用service中的方法了。
举个例子会更容易说明。
导出services
1、确保com.howard.sample.HelloService里的MANIFEST.MF导入org.osgi.framework包
2、创建com.howard.sample.service.impl.HelloServiceActivator.java,代码如下:
HelloServiceActivator
ServiceRegistration
helloServiceRegistration;
@Override
helloService
=
new
HelloServiceImpl();
helloServiceRegistration
context.registerService(HelloService.class
.getName(),
helloService,
null);
helloServiceRegistration.unregister();
OK,我们就是用BundleContext的registerService方法注册service的。
这个方法需要三个参数。
∙service的接口名。
如果service实现了多个接口,那样你需要传入一个包含所有接口名的String数组。
在这里我们传入的是HelloService这个接口。
∙真正的service实现。
在例子中我们传了一个HelloServiceImpl实现。
∙service属性。
这个参数可以在有多个service实现同一个接口的情况下,消费者用来区分真正感兴趣的service。
3、最后一步就是修改HelloService的MANIFEST.MF文件,将Bundle-Activator改成com.howard.sample.service.impl.HelloServiceActivator
现在HelloServicebundle已经随时准备将HelloServiceImpl服务发布了。
OSGI容器启动HelloServiebundle的时候会让HelloServiceActivator运作,在那个时候将HelloServiceImpl注册到容器中,接下来就是创建消费者的问题了。
导入service
我们的消费者就是HelloWorldbundle,主要修改的就是其中的Activator.java,修改代码如下:
ServiceReference
helloServiceReference;
World!
!
helloServiceReference=context.getServiceReference(HelloService.class.getName());
helloService=(HelloService)context.getService(helloServiceReference);
System.out.println(helloService.sayHello());
context.ungetService(helloServiceReference);
代码很简单,就不多说了。
在运行之前我们在Run-->
RunConfigurations对话框里,把HelloWorld和HelloService这两个bundle前面的钩都打上。
然后运行时你会发现HelloServiceImpl.sayHello()方法已经被调用了。
在OSGI控制台输入ss并回车,所有容器内的bundle状态一目了然。
其中id为0的bundle是OSGI框架基础bundle,另两个就是HelloService和HelloWorld了,它俩的id是随机的,状态是ACTIVE也就是已启动状态。
假设HelloService的id为7,HelloWorld为8。
输入stop8就可以暂停bundl