目录
1.简介
2.基本概念
2.1 Bundle
2.1.1 隔离
2.1.2 交互
2.2 Service
3.框架felix使用
3.1加载并解析jar为Bundle
3.2 Bundle的Classloader的隔离机制以及类共享机制
3.2.1 maven bundle插件export
3.3 使用方B获取
OSGi(Open Service Gateway Initiative),The Dynamic Module System For Java,从这个定义可见,OSGi的主要职责就是为了让开发者能够构建动态化、模块化的Java系统。此处要讲的是利用这个技术解决Jar包冲突问题,在一个maven项目中如果依赖的jar包存在版本冲突,或者其传递的依赖冲突,会被maven仲裁出一个来加载,因为其类全限定名完全一样只能加载一个。利用OSGI核心原理就是通过不同的类加载器(类似于命名空间)来隔离同一个类(全限定名)的不同版本,从而解决冲突。
Bundle就是一个特殊的jar包,特殊的在META-INF目录下的MANIFEST.MF文件中加入了OSGi特定描述。如下
为什么要隔离各个bundle呢?由于应用一旦臃肿,依赖很多jar包,jar包传递的依赖可能存在冲突。
核心原理是利用ClassLoader当作命名空间,每个Bundle都有自己独立于其他Bundle的ClassLoader,正因为这样,各个Bundle内部的类是隔离的。
一个孤岛注定啥也干不了,一个独立的budle有啥用呢?引入一个bundle一定是使用方需要用到,怎样使用呢?
一个Bundle可能会用到另外Bundle的类,Bundle之间是要交互的,这是怎么实现的呢?本文主要介绍Service方式
Bundle间交互方式 | |
---|---|
1.通过Package的Export和Imoprt来进行 | 提供者Bundle对外暴露(Export)自己的一个或多个Package, 使用者Bundle则根据自己的需要导入(Import)一个或多个Package。 |
2.通过Service的方式进行 | 提供者Bundle作为Service,对外提供Servcie, 使用者可以查找到其Service,并使用这个Service。 提供/使用Service方式:
|
一个OSGi Service就是注册到OSGi框架中的一个Java对象。在注册的时候可以设置这个Service的属性。而在获取Service的时候可以根据属性进行过滤(Filter)。Bundle可以通过BundleContext去注册Service或去查询Service。
是不是很像一个Bean之于Spring框架的ApplicationContext。基于OSGi的系统通常采用Module+Service的方式,要通过Bundle的BundleActivator来注册服务。另外的Bundle通过BundleContext来获取服务。
类似于Spring的ApplicationContext,里面注册了一些bundle暴露的Service,可以从context获取,并调用
以org.apache.felix.framework为例
在osgi模块下新增一个module,<packaging>bundlepackaging>
加入依赖的第三方库,并修改其插件配置,加入bundle插件。
org.apache.felix
maven-bundle-plugin
2.4.0
true
${pom.groupId}.${pom.artifactId}
${pom.name}
${pom.version}
com.xx.xxx.osgi.example.activator.ThirdPartyActivator
com.xx.xxx.osgi.example.Service;version="${project.version}"
org.osgi.framework
*;scope=compile|runtime
每个Bundle的生命周期被框架管理,在解析后会调用激活器的start()的方法,start(),此处start注册了Service,并初始化。
public void start(BundleContext paramBundleContext) throws Exception {
System.out.println("CebBankProvisionActivator.start.begin");
ServiceRegistration s = paramBundleContext.registerService(xxxOsgiService.class,
new xxxOsgiService(), null);
registrations.add(s);
xxxOsgiService third =
(xxxOsgiService) paramBundleContext.getService(s.getReference());
third.init();
}
由于暴露的Service需要被Bundle B使用,Service可以实现BundleB的接口ApiService,提供者A需要在其依赖中添加ApiService类所在的jar包,并将其scope写为provided,这样就不会加入到Bundle A。在使用时只要通过BundleContext获取Service就可以了。
ApiService osgiService = (ApiService)OsgiServiceRepository.getOsgiService(className);
public static Object getOsgiService(String className) {
BundleContext bundleContext = BundleContextHolder.getBundleContext();
if (bundleContext == null) {
return null;
}
ServiceReference serviceReference = bundleContext.getServiceReference(className);
if (serviceReference != null ) {
Object service = bundleContext.getService(serviceReference);
return service;
}
return null;
}