简体   繁体   English

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

[英]Access static method from classes loaded by different ClassLoaders

I have two classes ( A and B ) which are loaded by different ClassLoaders . 我有两个类( AB ),由不同的ClassLoaders Furthermore, I have a third class, which providers static getter and setter methods. 此外,我有第三个类,它提供静态getter和setter方法。 I hope following picture can clarify the situation: 我希望以下图片可以澄清情况:

在此输入图像描述

The Data class looks as following: Data类如下所示:

public class Data {

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

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

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

In class A , I want to set the static value of Data and in B I want to retrieve this value. A类中,我想设置Data的静态值,在B我想要检索这个值。 However, in B I always get the original value (which is "<fill in>" ). 但是,在B我总是得到原始值(即"<fill in>" )。 I only have a basic understanding of ClassLoader s, so I'm not too sure what is going on under the hood. 我对ClassLoader只有基本的了解,所以我不太清楚幕后发生了什么。 I thought that both ClassLoaders ( clA and clB ) will propagate to their parent ClassLoader and that I will get the same Data class in both. 我认为ClassLoaders( clAclB )都将传播到它们的父ClassLoader ,并且我将在两者中获得相同的Data类。 Can anyone give me some feedback on the behavior or point me in the direction to look at? 任何人都可以给我一些关于行为的反馈或指出我的方向来看待?

Update 更新

When I print the hashCode() of both Data classes, I get different values for them (meaning obviously I don't get access the same class). 当我打印两个Data类的hashCode()时,我得到了不同的值(显然我不能访问同一个类)。 Is there and easy way to illustrate the ClassLoader hierarchy? 是否有简单的方法来说明ClassLoader层次结构?

If your question is how to illustrate or visualize the classloader hierarchy for objects, then you can walk up each classes classloader in code. 如果您的问题是如何说明或可视化对象的类加载器层次结构,那么您可以在代码中向上遍历每个类类加载器。 You mentioned that you are using groovy, so an example would look like: 你提到你正在使用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();
        }
    }

}

I think you will find, in your code, the two Data objects are actually not loaded from the same classloader, which is why they have different static variables. 我想你会发现,在你的代码中,两个Data对象实际上并没有从同一个类加载器加载,这就是为什么它们有不同的静态变量。

I put together a sample that has 我把一个样本放在一起

  • Main (loaded from parent classloader) Main(从父类加载器加载)
  • DataObj with a static String (loaded also from parent classloader) 带有静态String的DataObj(也从父类加载器加载)
  • LoadA, which instantiates a copy of DataObj (loaded from child classloader A) LoadA,实例化DataObj的副本(从子类加载器A加载)
  • LoadB, which instantiates a copy of DataObj (loaded from child classloader B) LoadB,实例化DataObj的副本(从子类加载器B加载)

I see that while LoadA and LoadB have different classloaders, the DataObj and the static variable come from a common classloader. 我看到,虽然LoadA和LoadB有不同的类加载器,但DataObj和静态变量来自一个通用的类加载器。

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

The Main object in groovy: 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);

The results are: 结果是:

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

You see that LoadA and LoadB both have different classloaders, but they share a parent classloader. 您看到LoadA和LoadB都有不同的类加载器,但它们共享一个父类加载器。

The parent classloader loads the DataObj for both instances of the LoadA.dataObj and LoadB.dataObj. 父类加载器为LoadA.dataObj和LoadB.dataObj的两个实例加载DataObj。

LoadA.dataObj and LoadB.dataObj have different hashcodes. LoadA.dataObj和LoadB.dataObj具有不同的哈希码。

However, LoadA.dataObj.data and LoadB.dataObj.data have the same hashcode, because this is the static object. 但是,LoadA.dataObj.data和LoadB.dataObj.data具有相同的哈希码,因为这是静态对象。 They also have the same value. 它们也具有相同的价值。 LoadB instantiates it's dataObj last and sets the string to "Loaded By B" LoadB最后实例化它的dataObj并将字符串设置为“Loaded By B”

I think Lucas actually answered your question for illustrating the hierarchy. 我认为卢卡斯实际上已经回答了你的问题来说明等级。 I want to add my answer only to clear up some reminders of the question 我想添加我的答案只是为了澄清一些问题的提示

In Java the pair (defining classloader,class name) is unique. 在Java中,对(定义类加载器,类名)是唯一的。 Defining classloader here means the loader, which actually realizes the class as class from bytecode. 这里定义类加载器意味着加载器,它实际上是从字节码中将类实现为类。 This uniqueness means a classloader defining the class X cannot define a second class X, it must have a different name. 这种唯一性意味着定义类X的类加载器无法定义第二个类X,它必须具有不同的名称。 But another classloader can define the class. 但是另一个类加载器可以定义类。 ClassLoaders are structured in a kind of tree (it is not really a DAG, but that goes to far here) and a classloader is supposed to ask its parent first if queried for a class. ClassLoader是以一种树形式构建的(它实际上并不是DAG,但在这里很远),并且如果查询了类,则类加载器应首先询问其父级。 So it can happen, that Data exists twice, for example once in cIA and once in cIB. 因此,可能会发生数据存在两次,例如一次在cIA中,一次在cIB中。 To avoid this you usually want to have Data being defined by a classloader which is a parent to cIA and cIB. 为避免这种情况,您通常希望数据由类加载器定义,类加载器是cIA和cIB的父类。 That is assuming the two loaders behave according to the classloader constraints, like asking parents first. 这假设两个加载器的行为符合类加载器约束,就像先询问父母一样。

Since this is also about a Groovy script, but there are no real details given about the setup my assumption is that ciB has no parent that knows Data and that the library was given in to the url of the GroovyClassLoader used and that you used GroovyShell. 由于这也是关于Groovy脚本,但是没有给出关于设置的真实细节,我的假设是ciB没有知道Data的父级,并且库被提供给所使用的GroovyClassLoader的url并且你使用了GroovyShell。 What should be done instead is that GroovyShell is instantiated with one of the classloader taking arguments and that this classloader is a child to the loader defining Data, which is also a parent to cIA (parent can be the same loader in all cases I used the term parent). 应该做的是GroovyShell实例化其中一个类加载器接受参数,并且这个类加载器是定义Data的加载器的子类,它也是cIA的父类(在我使用的所有情况下,父类可以是相同的加载器父母一词)。

A warning... GroovyClassLoader is not a fully well behaving class loader. 警告...... GroovyClassLoader不是一个表现良好的类加载器。 It will prefere defined classes (for example through a script) over classes from the parent. 它将优先于父类的定义类(例如通过脚本)。 If you have a script with the class Data in it, it will use that Data class, even if the parent is normally the defining classloader 如果你有一个包含类Data的脚本,它将使用该Data类,即使父类通常是定义的类加载器

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 在Spring Boot和Logback中由不同的Classloader加载的类 - Classes loaded by different Classloaders in Spring Boot and Logback 检测不同ClassLoader加载的类的Class对象等效性 - Detecting Class object equivalence for classes loaded by different ClassLoaders Spring Data,EclipseLink和SimpleLoadTimeWeaver; 由不同的ClassLoader加载的域类 - Spring Data, EclipseLink, and SimpleLoadTimeWeaver; domain classes loaded by different ClassLoaders 使用不同的类加载器加载类,以便在不需要时从JVM卸载它们 - loading classes with different classloaders to unload them from the JVM when not needed 为什么不同自定义类加载器加载的类不能互相调用 - Why the classes loaded by different custom classloaders can't invoke each other 更改对不同类加载器加载的单例类的影响 - Impact of changes on singleton class loaded by different classloaders ClassLoader:两个ClassLoader及其类之间的访问级别 - ClassLoader: Access level between two ClassLoaders and their classes 当(在 Java 中)static 方法和具有相同名称的实例方法存在于不同的类中时,我无法访问该方法 - When (in Java) a static method and an instance method with the same name exist in different classes, I cannot access the method EnumMap具有来自不同类加载器的键的奇怪行为 - Strange behavior of EnumMap with keys from different classloaders Java Classloaders-在私有类内调用静态方法 - Java Classloaders - Invoking a static method inside a private class
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM