两个 Class 由 ClassLoader 加载,它们不能相互调用

Two Class Loaded by ClassLoader, They Can Not Call Each Other

假设你有两个 java class

package package1;

public class SampleClass1 {

    public SampleClass1() {

        System.out.println("Sample Class Loaded and Called");
    }
}

package package1.package2;

import package1.SampleClass1;

public class SampleClass2 {

    public SampleClass2() {

        new SampleClass1();
        System.out.println("Sample Class 2 Loaded and Called");
    }
}

并通过 ClassLoader 加载两个 classes:

package load;

public class LoadClassFromByteData extends ClassLoader {

    private byte[] classByteData;

    public LoadClassFromByteData(byte[] classByteData) {
        this.classByteData = classByteData;
    }

    public Class<?> getLoadedClass(String className) {

        return defineClass(className, classByteData, 0, classByteData.length);
    }
}

//

Class<?> loadedClass1 = loadClassFromByteData.getLoadedClass("package1.SampleClass1");
Class<?> loadedClass2 = loadClassFromByteData.getLoadedClass("package1.package2.SampleClass2");

当我这样调用 SampleClass1 的构造函数时:

Constructor<?> mainCons1 = loadedClass1.getConstructor();
mainCons1.newInstance();

成功,打印 "Sample Class Loaded and Called" 但是当我调用 loadedClass2 的构造函数从 SampleClass2 字节数据加载时,出现如下错误:

Constructor<?> mainCons2 = loadedClass2.getConstructor();
mainCons2.newInstance();

// 是的,首先我加载 SampleClass1,然后我加载 SampleClass2,最后我调用 SampleClass2 构造函数。

Exception in thread "main" java.lang.reflect.InvocationTargetException
    at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)
    at db.recorder.deneme.main(deneme.java:48)
Caused by: java.lang.NoClassDefFoundError: package1/SampleClass1
    at package1.package2.SampleClass2.<init>(SampleClass2.java:9)
    ... 5 more
Caused by: java.lang.ClassNotFoundException: package1.SampleClass1
    at java.base/java.lang.ClassLoader.findClass(ClassLoader.java:718)
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:588)
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
    ... 6 more

我想添加这个信息,两个 类 字节数据存储在数据库中,我从数据库中获取 class 字节数据。实际上,该项目没有任何名为 package1 或 package2 的包!

感谢您的回答和建议。

您需要提供父类加载器。

   public static class LoadClassFromByteData extends ClassLoader {

        final ConcurrentHashMap<String, Class<?>> definedClassesByName = new ConcurrentHashMap<>();

        LoadClassFromByteData(ClassLoader parent) {
            super(parent);
        }

        public Class<?> getLoadedClass(String className, byte[] classAsBytes) {

            return this.definedClassesByName.computeIfAbsent(className,
                    cn -> defineClass(cn, classAsBytes, 0, classAsBytes.length));
        }
    }

    @Test
    public void test() throws IOException, InstantiationException, IllegalAccessException {

        final byte[] sampleClass1Bytes = Files.readAllBytes(SAMPLE_CLASS1_CLASS_FILE_PATH);
        final byte[] sampleClass2Bytes = Files.readAllBytes(SAMPLE_CLASS2_CLASS_FILE_PATH);

        final ClassLoader contextClassLoader = Thread.currentThread()
            .getContextClassLoader();
        final LoadClassFromByteData loadClassFromByteData = new LoadClassFromByteData(contextClassLoader);
        final Class<?> sampleClass1 = loadClassFromByteData.getLoadedClass("package1.SampleClass1", sampleClass1Bytes);
        final Object sampleClass1Instance = sampleClass1.newInstance();
        final Class<
            ?> sampleClass2 = loadClassFromByteData.getLoadedClass("package1.package2.SampleClass2", sampleClass2Bytes);
        final Object newInstance = sampleClass2.newInstance();
    }