我有两个类(A
和B
),它们由不同的ClassLoaders
加载。此外,我有第三个类,它提供静态getter和setter方法。我希望以下图片可以澄清情况:
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(clA
和clB
)都会传播到它们的父ClassLoader
,并且我将在两者中获得相同的Data
类。任何人都可以给我一些关于这种行为的反馈,还是指出我要看的方向?
当我打印两个hashCode()
类的Data
时,我会得到不同的值(显然我不能访问同一个类)。是否有简单的方法来说明ClassLoader
层次结构?
答案 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对象实际上并没有从同一个类加载器加载,这就是为什么它们有不同的静态变量。
我把一个有
的样本放在一起我看到虽然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类,即使父类通常是定义的类加载器