Assume you have two 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");
}
}
and load two classes by ClassLoader something like that:
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");
when i call constructor of SampleClass1 like that:
Constructor<?> mainCons1 = loadedClass1.getConstructor();
mainCons1.newInstance();
successfully, printing "Sample Class Loaded and Called" but when i call constructor of loadedClass2 load from SampleClass2 byte data i have an error like:
Constructor<?> mainCons2 = loadedClass2.getConstructor();
mainCons2.newInstance();
// And yes, first i load SampleClass1 then i load SampleClass2, finally i call SampleClass2 constructor.
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
I want to add this information, two Classes byte data stored in DB, and i get byte data of classes from DB. Actually, the project has not any package named package1 or package2 !
Thanks for your answers and advices.
You need to supply the parent classloader.
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();
}