调用通过不同 ClassLoader 加载的 class 的静态方法无效。这是预期的吗?

Calling static method of class loaded via different ClassLoader isn't working. Is this expected?

我有以下 class,它使用一个简单的客户类加载器加载一个 class,然后直接在其上调用静态方法:

public class App {

    public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        MyCustClassLoader loader = new MyCustClassLoader();
        Class<?> c = loader.findClass("classloading.FooObservable");
        Object ob = c.newInstance();
        Method md = c.getDeclaredMethod("addListener", Listener.class);

        FooListener fooListener = new FooListener("app class loader");
//        md.invoke(ob, fooListener); <<< works it I uncomment this.
        FooObservable.addListener(fooListener);

        md = c.getMethod("fooDidSomething");
        md.invoke(ob);
        md.invoke(ob);
        md.invoke(ob);
    }

}

我的自定义 class 加载器如下所示:

package classloading;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

public class MyCustClassLoader extends ClassLoader {
    @Override
    public Class<?> findClass(String name) {
        byte[] bt = loadClassData(name);
        return defineClass(name, bt, 0, bt.length);
    }
    private byte[] loadClassData(String className) {
        //read class
        InputStream is = getClass().getClassLoader().getResourceAsStream(className.replace(".", "/")+".class");
        ByteArrayOutputStream byteSt = new ByteArrayOutputStream();
        //write into byte
        int len =0;
        try {
            while((len=is.read())!=-1){
                byteSt.write(len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        //convert into byte array
        return byteSt.toByteArray();
    }

}

有问题的静态方法在 class FooObservable 中定义,其中包含一个侦听器列表和一个可以调用以通知侦听器发生了某些事情的通知方法:

package classloading;

import java.util.ArrayList;
import java.util.List;    

public class FooObservable {

        private static List<Listener> listeners = new ArrayList<Listener>();
        int x = 0;

        public static void addListener(Listener l) {
            listeners.add(l);
        }

        public void fooDidSomething() {
            x++;
            for (Listener l : listeners) {
                l.notify(x);
            }
        }

    }

它似乎不起作用,因为没有打印任何内容。但是,当我取消注释上面的行 md.invoke class App 并注释掉静态调用 FooObservable.addListener 时,它起作用了,我看到:

foo listener (app class loader) notified: 1
foo listener (app class loader) notified: 2
foo listener (app class loader) notified: 3

为什么会这样?是否归结为当该行未注释时,针对 FooObservable 调用静态调用 addListener,它是使用 Java AppClassLoader 加载的,因此重新加载了一个空列表听众?

这一行:

FooObservable.addListener(fooListener);

正在创建一个不同的 FooObservable 实例,由默认的 classloader 创建。因为它是一个不同的 classloader,所以它是一个完全不同的对象。针对 md.<> 的调用是针对来自您的 class 加载程序的 FooObservable。这些是同一个静态 class 的两个不同实例(这是 classloader 的一部分 - 你可以有相同 class 的不同实例)。

如果你调用 FooObservable.fooDidSomething() 而不是调用 md.invoke(ob),你会看到一个成功的结果(来自父 classloader 实例)。

如果不调用 md.addListener(注释掉的行),您的 classloader 加载的版本没有监听器。