OSGi 中的 Java 类加载器使用

Posted

技术标签:

【中文标题】OSGi 中的 Java 类加载器使用【英文标题】:Java classloader usage in OSGi 【发布时间】:2016-04-16 17:04:50 【问题描述】:

我有一个关于在 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)] [Client: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-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-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,class Wrapper就是指这2个类。

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 捆绑类加载器机制的一个非常简单的实现?对吧?

【问题讨论】:

你能显示为你的 .bnd 文件或清单或 pom 吗?也许你设置了错误的捆绑激活器类。 【参考方案1】:

你说:“据我所知,当A类引用B类时,B类的类加载器和A类的类加载器是一样的。但是在OSGi中,好像不是这样的。”

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

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

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

【讨论】:

我不认为您对“您编写的每个类都从 java.lang.Object 扩展。您的类由应用程序类加载器加载”是正确的。这是因为“我的班级”仍在类路径中,系统类加载器可以找到它,因此类加载器原来是应用类加载器。如果“my class”不在类路径下,只有我自定义的classloader才能找到,那么“my class”的classloader不应该是app classloader。 嘿,我已经使用 Java 编码 18 年了,并且写过一本关于 OSGi 的书;我是对的。如果您有一个自定义类加载器,那么该类加载器仍然有一个父类,它仍然委托给引导类加载器。 嗨,巴特利特,无意冒犯。不知道我表达的对不对。但是我刚刚写了一些关于这个问题的代码,我发现类加载器可以被“继承”。让我把代码贴出来,看看有没有问题:) 好的,它被称为“父委托”而不是继承,但如果这有助于您更好地理解它,那很好。【参考方案2】:

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

让我们来看看你的场景。

捆绑包 osgi-server 包含类 com.cisco.ruan.server.HelloService 它还导出包 com.cisco.ruan.server。 捆绑 osgi-client 导入包 com.cisco.ruan.server。当您在 osgi-client 的 Activator 中加载 HelloService 类时,会要求 osgi-client 的类加载器加载该类。它为包找到一个委托,并将加载委托给 osgi-server 的类加载器。然后这个类加载器是用户加载类。

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

【讨论】:

以上是关于OSGi 中的 Java 类加载器使用的主要内容,如果未能解决你的问题,请参考以下文章

类加载器

类加载器

在 OSGi 环境中,类路径和类加载器是如何设置的?

OSGi/Equinox 类加载器使用了意外的捆绑版本

在 OSGi 中更好地处理线程上下文类加载器

java类加载器(转)