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

时间:2019-01-09 13:43:42

标签: java classloader

假设您有两个java类

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加载两个类似的类:

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();

成功地,打印“加载并调用了示例类”,但是当我从SampleClass2字节数据中调用loadClass2加载的构造函数时,出现错误:

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字节数据存储在DB中,我从DB获取类的字节数据。实际上,该项目没有任何名为package1或package2的包!

感谢您的回答和建议。

1 个答案:

答案 0 :(得分:1)

您需要提供父类加载器。

   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();
    }