【OSGI】用OSGI解决JAR包冲突

目录

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;
    }

发布了132 篇原创文章 · 获赞 122 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/sarafina527/article/details/105219454