用户工具


作用

  1. 应用内模块的隔离
    1. 一个模块一个服务,一个服务的变更不影响其他服务(主要是二方库依赖)

隔离原理

bundle间之所以能隔离,因为每个bundle都有一个单的ClassLoader,且该ClassLoader的 parent ClassLoader为空(也就是说不会委派给其他ClassLoader)。但是每个bundle加载Import-Package时,会使用Export-Package的ClassLoader去加载。这也就保证了不同bundle间也可以共享类

运行方式

  1. all bundle模式:所有逻辑都以bundle的方式存在
  2. embedded 模式:在主应用中启动一个osgi框架 + 其他bundle

本文主要讲解embedded 模式 的使用

bundle间的调用

bundle间服务调用的方式 (主程序也是作为一个bundle来工作的)

通过反射调用

  1. bundle-service注册服务,bundle-cliet获取服务
  2. 根据方法名,反射调用方法

优点

  • 简单无依赖

通过共享接口调用

  • bundle-client bundle-server之间依赖同一个二方库(二方库中定义了服务的接口),bundle-server实现服务,bundle-client调用服务

缺点:

  • 实现复杂
    1. 接口要单独一二方库,被2个bundle依赖 (bundle-client mvn依赖的scope=provided)
    2. 配置bundle-server 暴露package
    3. 配置bundle-server 引入package

样例代码

bundle mvn配置

    <dependencies>

        <dependency>
            <groupId>org.apache.felix</groupId>
            <artifactId>org.apache.felix.framework</artifactId>
            <version>6.0.3</version>
        </dependency>

    </dependencies>
<build>
        <plugins>
            <plugin>
                <groupId>org.apache.felix</groupId>
                <artifactId>maven-bundle-plugin</artifactId>
                <!--<version>2.4.0</version>-->
                <extensions>true</extensions>
                <configuration>
                    <instructions>
                        <Bundle-Version>${project.version}</Bundle-Version>
                        <Bundle-SymbolicName>$(replace;${project.artifactId};-;_)</Bundle-SymbolicName>
                        <!--要暴露的包-->
                        <Export-Package>
                            aa.bb.cc.intf;version="${project.version}"
                        </Export-Package>
                        <!--要引入的包-->
                        <Import-Package>
                            org.osgi.framework
                        </Import-Package>
                        <Bundle-Activator>
                            aa.bb.cc.impl.App
                        </Bundle-Activator>
                    </instructions>
                </configuration>
            </plugin>
        </plugins>
    </build>

bundle中注册服务

public class App implements BundleActivator {
    private static BundleContext context;
    static BundleContext getContext() {
        return context;
    }

    @Override
    public void start(BundleContext bundleContext) throws Exception {
        System.out.println("start bundle  -- " + bundleContext);

        App.context = bundleContext;
        Hashtable<String, String> props = new Hashtable<String, String>();
        props.put("Language", "English");
        
        context.registerService(IHello.class.getName(), new DefaultHelloServiceImpl(), props);
    }

    @Override
    public void stop(BundleContext bundleContext) throws Exception {
        App.context = null;
        System.out.println("end bundle");
    }

    public class DefaultHelloServiceImpl implements IHello {

        @Override
        public String say() {
            return "Hello osgi,service";
        }
    }
}

主程序中通过反射调用服务

public static void main(String[] args) {
        try {
            final Map configMap = new HashMap();
            configMap.put(Constants.FRAMEWORK_STORAGE_CLEAN, "onFirstInit");

            Felix felix = new Felix(configMap);
            felix.init();

            final BundleContext context = felix.getBundleContext();

            // 加载一个bundle
            Bundle bundle = context.installBundle("file:/Users/fangqiang/Documents/osgibundle/server/target/server-1.0-SNAPSHOT.jar");
            // 启动bundle
            bundle.start();

            // 启动osgi框架
            felix.start();

            // 通过反的方法调用bundle中的服务
            for (ServiceReference<?> registeredService : bundle.getRegisteredServices()) {
                Object service = context.getService(registeredService);
                Method say = service.getClass().getMethod("say");
                System.out.println(say.invoke(service));
            }

        } catch (Exception ex) {
            System.exit(0);
        }
    }

主程序中通过共享接口调用

public static void main(String[] args) {
        try {
            final Map configMap = new HashMap();
            configMap.put(Constants.FRAMEWORK_STORAGE_CLEAN, "onFirstInit");

            // 程序中通过configMap配置要暴露的包 (如果是bundle中直接在pom.xml中的Export-Package中配置即可)
            configMap.put(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA, "aa.bb.common.intf; version=1.0.0.SNAPSHOT");

            HostActivator activator = new HostActivator();
            // 将主程序中的服务注册到osgi中 (如果没有服务要暴露给bundle,则不需要该配置)
            configMap.put(FelixConstants.SYSTEMBUNDLE_ACTIVATORS_PROP, Arrays.asList(activator));

            Felix felix = new Felix(configMap);
            felix.init();

            BundleContext context = felix.getBundleContext();

            // 加载一个bundle
            Bundle bundle = context.installBundle("file:/Users/fangqiang/Documents/osgibundle/server/target/server-1.0-SNAPSHOT.jar");
            // 启动bundle
            bundle.start();

            // 启动osgi框架
            felix.start();

            // 通过接口调用bundle中服
            System.out.println(((IHello1)context.getService(context.getServiceReference(IHello1.class.getName()))).say());

        } catch (Exception ex) {
            System.exit(0);
        }
    }

其他bundle中通过共享接口调用

public class App implements BundleActivator {

    @Override
    public void start(BundleContext ctx) {
        System.out.println("----------------hello client start---------------------");
        ServiceReference ref = ctx.getServiceReference(IHello.class.getName());
        IHello hello = (IHello) ctx.getService(ref);
        hello.say();
        System.out.println("----------------hello client start---------------------");
    }

    @Override
    public void stop(BundleContext ctx) throws Exception {

    }
}