从不同ClassLoader加载的类访问静态方法

时间:2014-11-30 20:59:32

标签: java classloader

我有两个类(AB),它们由不同的ClassLoaders加载。此外,我有第三个类,它提供静态getter和setter方法。我希望以下图片可以澄清情况:

enter image description here

Data类如下所示:

public class Data {

    private static String data = "<fill in>";

    public static void setData(String d) {
        data = d;
    }

    public static String getData() {
        return data;
    }
}

在课程A中,我想设置Data的静态值,而在B我想要检索此值。但是,在B中,我总是得到原始值("<fill in>")。我对ClassLoader s只有基本的了解,所以我不太清楚幕后发生了什么。我认为ClassLoaders(clAclB)都会传播到它们的父ClassLoader,并且我将在两者中获得相同的Data类。任何人都可以给我一些关于这种行为的反馈,还是指出我要看的方向?

更新

当我打印两个hashCode()类的Data时,我会得到不同的值(显然我不能访问同一个类)。是否有简单的方法来说明ClassLoader层次结构?

2 个答案:

答案 0 :(得分:2)

如果您的问题是如何说明或可视化对象的类加载器层次结构,那么您可以在代码中向上遍历每个类类加载器。你提到你正在使用groovy,所以一个例子看起来像:

def showObjectClassLoaderHierarchy(Object obj) {
    def classLoader = showClassLoaderHierarchy(obj.getClass().getClassLoader());
    showClassLoaderHierarchy(classLoader);
}

def showClassLoaderHierarchy(ClassLoader loader) {

    if (loader != null) {
        println "Classloader: " + loader.hashCode();
        while (loader.getParent() != null) {
              loader = loader.getParent();
          println "    Child of: " + loader.hashCode();
        }
    }

}

我认为你会发现,在你的代码中,两个Data对象实际上并没有从同一个类加载器加载,这就是为什么它们有不同的静态变量。

我把一个有

的样本放在一起
  • Main(从父类加载器加载)
  • 带有静态字符串的DataObj(也从父类加载器加载)
  • LoadA,实例化DataObj的副本(从子类加载器A加载)
  • LoadB,实例化DataObj的副本(从子类加载器B加载)

我看到虽然LoadA和LoadB有不同的类加载器,但DataObj和静态变量来自一个通用的类加载器。

完整代码:https://github.com/lucasmcgregor/groovy_classloader_test

groovy中的主要对象:

import java.lang.ClassLoader;
import java.net.URLClassLoader;
import java.net.URL;

def showObjectClassLoaderHierarchy(Object obj) {
        def classLoader = showClassLoaderHierarchy(obj.getClass().getClassLoader());
        showClassLoaderHierarchy(classLoader);
}

def showClassLoaderHierarchy(ClassLoader loader) {

        if (loader != null) {
            println "Classloader: " + loader.hashCode();
            while (loader.getParent() != null) {
                  loader = loader.getParent();
                  println "    Child of: " + loader.hashCode();
            }
        }

}

println "Setting up child classLoaders A and B...";

def URL[] urlsA = [new URL("file:///tmp/cla/")];
def classLoaderA = new URLClassLoader(urlsA, this.getClass().getClassLoader());

def URL[] urlsB = [new URL("file:///tmp/clb/")];
def classLoaderB = new URLClassLoader(urlsB, this.getClass().getClassLoader());


println "Classloader A heirachry:";
showClassLoaderHierarchy(classLoaderA);

println "Classloader B: ";
showClassLoaderHierarchy(classLoaderB);

println "";
println "Now loading Load classes A and B from seperate classloaders:";
def loadA = classLoaderA.loadClass("LoadA").newInstance();
def loadB = classLoaderB.loadClass("LoadB").newInstance();

print "LoadA: heirachry";
showObjectClassLoaderHierarchy(loadA);
print "LoadB: heirachry";
showObjectClassLoaderHierarchy(loadB);

println "";
println "Now pulling the data objects from both and comparing classloders and static data: ";
def dobjA = loadA.getDataObj();
def dobjB = loadB.getDataObj();

println "dataA static field:" + dobjA.getData();
println "dataA static field hashcode: " + dobjA.getData().hashCode();
println "dataA hashcode: " + dobjA.hashCode();
println "dataA classloader: ";
showObjectClassLoaderHierarchy(dobjA);

println "dataB static field: " + dobjB.getData();
println "dataB static field hashcode: " + dobjB.getData().hashCode();
println "dataB hashcode: " + dobjB.hashCode();
println "dataB classLoader:";
showObjectClassLoaderHierarchy(dobjB);

结果是:

Setting up child classLoaders A and B...
Classloader A heirachry:
Classloader: 1926764753
    Child of: 1163157884
    Child of: 1022308509
Classloader B:
Classloader: 846238611
    Child of: 1163157884
    Child of: 1022308509

Now loading Load classes A and B from seperate classloaders:
LoadA: heirachryClassloader: 1926764753
    Child of: 1163157884
    Child of: 1022308509
LoadB: heirachryClassloader: 846238611
    Child of: 1163157884
    Child of: 1022308509

Now pulling the data objects from both and comparing classloders and static data:
dataA static field:Loaded By B
dataA static field hashcode: 1828548084
dataA hashcode: 2083117811
dataA classloader:
Classloader: 1163157884
    Child of: 1022308509
dataB static field: Loaded By B
dataB static field hashcode: 1828548084
dataB hashcode: 157683534
dataB classLoader:
Classloader: 1163157884
    Child of: 1022308509

您看到LoadA和LoadB都有不同的类加载器,但它们共享一个父类加载器。

父类加载器为LoadA.dataObj和LoadB.dataObj的两个实例加载DataObj。

LoadA.dataObj和LoadB.dataObj具有不同的哈希码。

但是,LoadA.dataObj.data和LoadB.dataObj.data具有相同的哈希码,因为这是静态对象。它们也具有相同的价值。 LoadB最后实例化它的dataObj并将字符串设置为&#34;由B加载&#34;

答案 1 :(得分:1)

我认为卢卡斯实际上已经回答了你的问题来说明等级。我想添加我的答案只是为了澄清一些问题的提醒

在Java中,这对(定义类加载器,类名)是唯一的。这里定义类加载器意味着加载器,它实际上是从字节码中将类实现为类。这种唯一性意味着定义类X的类加载器无法定义第二个类X,它必须具有不同的名称。但是另一个类加载器可以定义类。 ClassLoader是以一种树形式构建的(它实际上并不是DAG,但在这里很远),并且如果查询了类,则类加载器应首先询问其父级。因此,可能会发生数据存在两次,例如一次在cIA中,一次在cIB中。为避免这种情况,您通常希望数据由类加载器定义,类加载器是cIA和cIB的父类。这假设两个加载器的行为符合类加载器约束,就像先询问父母一样。

由于这也是关于Groovy脚本,但是没有给出关于设置的真实细节,我的假设是ciB没有知道Data的父级,并且该库被提供给所使用的GroovyClassLoader的url并且你使用了GroovyShell。应该做的是GroovyShell实例化其中一个类加载器接受参数,并且这个类加载器是定义Data的加载器的子类,它也是cIA的父类(在我使用的所有情况下,父类可以是相同的加载器父母一词)。

警告...... GroovyClassLoader不是一个表现良好的类加载器。它将优先于父类的定义类(例如通过脚本)。如果你有一个包含类Data的脚本,它将使用该Data类,即使父类通常是定义的类加载器