目录
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获取
1.简介
OSGi(Open Service Gateway Initiative),The Dynamic Module System For Java,从这个定义可见,OSGi的主要职责就是为了让开发者能够构建动态化、模块化的Java系统。此处要讲的是利用这个技术解决Jar包冲突问题,在一个maven项目中如果依赖的jar包存在版本冲突,或者其传递的依赖冲突,会被maven仲裁出一个来加载,因为其类全限定名完全一样只能加载一个。利用OSGI核心原理就是通过不同的类加载器(类似于命名空间)来隔离同一个类(全限定名)的不同版本,从而解决冲突。
2.基本概念
2.1 Bundle
Bundle就是一个特殊的jar包,特殊的在META-INF目录下的MANIFEST.MF文件中加入了OSGi特定描述。如下
2.1.1 隔离
为什么要隔离各个bundle呢?由于应用一旦臃肿,依赖很多jar包,jar包传递的依赖可能存在冲突。
核心原理是利用ClassLoader当作命名空间,每个Bundle都有自己独立于其他Bundle的ClassLoader,正因为这样,各个Bundle内部的类是隔离的。
2.1.2 交互
一个孤岛注定啥也干不了,一个独立的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方式:
|
2.2 Service
一个OSGi Service就是注册到OSGi框架中的一个Java对象。在注册的时候可以设置这个Service的属性。而在获取Service的时候可以根据属性进行过滤(Filter)。Bundle可以通过BundleContext去注册Service或去查询Service。
是不是很像一个Bean之于Spring框架的ApplicationContext。基于OSGi的系统通常采用Module+Service的方式,要通过Bundle的BundleActivator来注册服务。另外的Bundle通过BundleContext来获取服务。
2.3 BundleContext
类似于Spring的ApplicationContext,里面注册了一些bundle暴露的Service,可以从context获取,并调用
3.框架felix使用
以org.apache.felix.framework为例
3.1加载并解析jar为Bundle
在osgi模块下新增一个module,<packaging>bundle</packaging>
加入依赖的第三方库,并修改其插件配置,加入bundle插件。
3.2 Bundle的Classloader的隔离机制以及类共享机制
3.2.1 maven bundle插件export
- 配置暴露的包和引入的包,由于com.xx.xxx.osgi.example.Service这个包跟导入的第三方包处于同一个模块,会被框架绑定为一个Bundle,Service包中的类会与第三方被同一个ClassLoader加载,即处于一个命名空间。
- 配置激活器,激活器会将Service包中的Service类注册到BundleContext
<plugin><groupId>org.apache.felix</groupId><artifactId>maven-bundle-plugin</artifactId><version>2.4.0</version><extensions>true</extensions><configuration><instructions><Bundle-SymbolicName>${pom.groupId}.${pom.artifactId}</Bundle-SymbolicName><Bundle-Name>${pom.name}</Bundle-Name><Bundle-Version>${pom.version}</Bundle-Version><Bundle-Activator>com.xx.xxx.osgi.example.activator.ThirdPartyActivator</Bundle-Activator><Export-Package>com.xx.xxx.osgi.example.Service;version="${project.version}"</Export-Package><Import-Package>org.osgi.framework</Import-Package><Require-Bundle></Require-Bundle><Embed-Dependency>*;scope=compile|runtime</Embed-Dependency></instructions></configuration>
</plugin>
每个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();}
3.3 使用方B获取
由于暴露的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;}