OSGi中的Java类加载器用法

时间:2016-01-12 07:18:56

标签: java osgi classloader osgi-bundle karaf

我对OSGi中Java ClassLoader的使用有疑问。

我写了两个OSGi包,即服务器包和客户端包。

在服务器包中,我实现了BundleActivator,如:

public class Activator implements BundleActivator {

    public void start(BundleContext context) {
        System.out.println("[Server:Activator.java:26] " + Activator.class.getClassLoader());
        context.registerService(HelloService.class, new HelloService(), null);
    }

    public void stop(BundleContext context) {
        System.out.println("Stopping the bundle");
    }
}

在客户端捆绑中,我实现了BundleActivator,如:

public class Activator implements BundleActivator {

    public void start(BundleContext context) {
        ServiceReference<HelloService> ref = context.getServiceReference(HelloService.class);
        HelloService service = context.getService(ref);
        System.out.println("[Client:Activator.java:48] " + HelloService.class.getClassLoader());
        System.out.println("[Client:Activator.java:49] " + Activator.class.getClassLoader());
    }

    public void stop(BundleContext context) {
        System.out.println("Stopping the bundle");
    }
}

当我启动OSGi时,控制台输出:

  

[服务器:Activator.java:26] org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader@56b161a [osgi-server:1.0.0(id = 54)]   [客户:Activator.java:48] org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader@56b161a [osgi-server:1.0.0(id = 54)]   [客户:Activator.java:49] org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader@3a1b72aa [osgi-client:1.0.0(id = 55)]

正如您所看到的,无论是在服务器端还是客户端,加载HelloService的类加载器始终是 DefaultClassLoader @ 56b161a

我无法理解这一点。据我所知,当在A类中引用B类时,B类的类加载器与A类的类加载器相同。但在OSGi中,似乎并非如此。

你能开导我吗?有没有我想念的Java ClassLoader?或者OSGi做了些棘手的事情吗?

服务器包的MANIFEST是:

Manifest-Version: 1.0
Bnd-LastModified: 1452582379580
Build-Jdk: 1.7.0_45
Built-By: haoruan
Bundle-Activator: com.cisco.ruan.server.Activator
Bundle-Description: osgi-server OSGi bundle project.
Bundle-ManifestVersion: 2
Bundle-Name: osgi-server Bundle
Bundle-SymbolicName: osgi-server
Bundle-Version: 1.0
Created-By: Apache Maven Bundle Plugin
Export-Package: com.cisco.ruan.server;version="1.0";uses:="org.osgi.fram
 ework"
Import-Package: org.osgi.framework;version="[1.7,2)"
Require-Capability: osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.5))"
Tool: Bnd-3.0.0.201509101326

客户端捆绑的MANIFEST是:

Manifest-Version: 1.0
Bnd-LastModified: 1452582396099
Build-Jdk: 1.7.0_45
Built-By: haoruan
Bundle-Activator: com.cisco.ruan.client.Activator
Bundle-Description: osgi-client OSGi bundle project.
Bundle-ManifestVersion: 2
Bundle-Name: osgi-client Bundle
Bundle-SymbolicName: osgi-client
Bundle-Version: 1.0
Created-By: Apache Maven Bundle Plugin
Export-Package: com.cisco.ruan.client;version="1.0";uses:="com.cisco.rua
 n.server,org.osgi.framework"
Import-Package: com.cisco.ruan.server;version="[1.0,2)",org.osgi.framewo
 rk;version="[1.7,2)",org.slf4j;version="[1.7,2)"
Require-Capability: osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.5))"
Tool: Bnd-3.0.0.201509101326

=============================================== =======

嗨,尼尔,这是我刚才做的实验:

我有ClassA和ClassB,而类Wrapper引用这两个类。

public class Wrapper {

    public Wrapper() {
        showInfo();
    }

    public void showInfo() {
        System.out.println("[Wrapper.java:5] " + ClassA.class.getClassLoader());
        System.out.println("[Wrapper.java:8] " + ClassB.class.getClassLoader());
    }
}

我编写了自己的自定义类加载器MyClassLoader:

class MyClassLoader extends ClassLoader {
    private ClassLoader haocl;
    private ClassLoader ruancl;

    public MyClassLoader() {
        this.haocl = new HaoClassLoader();
        this.ruancl = new RuanClassLoader();
    }

    @Override
    public Class<?> loadClass(String name) throws ClassNotFoundException {

        if (name.endsWith("com.cisco.ruan.classloader.ClassA")) {
            return haocl.loadClass(name);
        }

        if (name.endsWith("com.cisco.ruan.classloader.ClassB")) {
            return ruancl.loadClass(name);
        }

        if (name.endsWith("Wrapper")) {
            InputStream is = null;
            try {
                is = new FileInputStream("/Users/haoruan/Desktop/Projects/cl-test/target/classes/com/cisco/ruan/classloader/Wrapper.class");
            } catch (Exception e) {
                e.printStackTrace();
            }
            byte[] bytes = null;
            try {
                bytes = ByteStreams.toByteArray(is);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return defineClass(name, bytes, 0, bytes.length);
        }

        return super.loadClass(name);

    }
}

然后我打电话给Class.forName("com.cisco.ruan.classloader.Wrapper", true, mcl).newInstance();,控制台输出:

  

[Wrapper.java:5] com.cisco.ruan.classloader.HaoClassLoader@248523a0   [Wrapper.java:8] com.cisco.ruan.classloader.RuanClassLoader@3c635421

因此,可以推断出ClassA和ClassB最初由MyClassLoader加载,然后由HaoClassLoader和RuanClassLoader实际加载。而且我认为这个实验可以看作是一个非常简单的OSGi捆绑类加载器机制的实现?正确?

2 个答案:

答案 0 :(得分:8)

这在OSGi中完全正常。在OSGi中,每个包有一个类加载器。此类加载器为bundle中的所有类提供服务。对于bundle之外的所有类,都有Import-Package定义。在运行时,每个包导入都连接到导出包的包。当加载来自这样的包的类时,加载被委托给其他bundle类加载器。

让我们看看你的情景。

Bundle osgi-server包含类com.cisco.ruan.server.HelloService它还导出包com.cisco.ruan.server。 Bundle osgi-client导入包com.cisco.ruan.server。当您在osgi-client的Activator中加载HelloService类时,要求osgi-client的类加载器加载该类。它找到了一个包和委托加载到osgi-server的类加载器的委托。然后,该类加载器用户可以加载该类。

这是OSGi中的默认行为,如果您认为它通过它很有意义。

答案 1 :(得分:2)

你说:“据我所知,当A类引用B类时,B类的类加载器与A类的类加载器相同。但在OSGi中,似乎不是这样。”

这不是关于Java类加载器的真实陈述......无论您是否使用OSGi。

例如,您编写的每个类都来自java.lang.Object。您的类由应用程序类加载器加载,但引导类加载器加载java.lang.Object。这是因为委托:一个类加载器可以让另一个类加载器代表它加载一个类。

在OSGi中,它完全是一回事。每个bundle都有一个类加载器,当你从另一个bundle导入一个包时,另一个bundle的类加载器用来加载它们。