当前位置: 代码迷 >> 综合 >> 【OSGI】用OSGI解决JAR包冲突
  详细解决方案

【OSGI】用OSGI解决JAR包冲突

热度:30   发布时间:2023-12-22 01:31:15.0

目录

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.通过PackageExportImoprt来进行

提供者Bundle对外暴露(Export)自己的一个或多个Package,

使用者Bundle则根据自己的需要导入(Import)一个或多个Package。

2.通过Service的方式进行

提供者Bundle作为Service,对外提供Servcie,

使用者可以查找到其Service,并使用这个Service。

提供/使用Service方式:

  1. 通过BundleContext(Bundle的上下文)来提供和获取;
  2. 一种是使用Declarative 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

  1. 配置暴露的包和引入的包,由于com.xx.xxx.osgi.example.Service这个包跟导入的第三方包处于同一个模块,会被框架绑定为一个Bundle,Service包中的类会与第三方被同一个ClassLoader加载,即处于一个命名空间。
  2. 配置激活器,激活器会将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;}