Как сохранить определение класса динамически сгенерированного Java-класса с использованием ASM?
Я расширил класс динамически, используя ASM. Я использовал метод defineClass без аргумента ProtectionDomain. Когда я пытаюсь использовать этот класс в другом классе, я получаю java.lang.NoClassDefFound Error. Как я могу сохранить или получить определение класса, который определен во время выполнения, используя ASM?
2 ответа
Вместо того, чтобы каждый раз создавать экземпляр DynamicClassLoader, его сохранение в статической переменной уровня класса решает эту проблему. Каждый раз, когда создавался новый загрузчик классов, ранее созданный класс не был найден.
Вот пример кода, у меня есть интерфейсный калькулятор
package com.Executor;
public interface Calculator {
int add(int left, int right);
}
ниже приведен код для динамического создания класса, который реализует и переопределяет метод.
package com.Executor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
public class DynamicClassWriter {
public static class DynamicClassLoader extends ClassLoader {
public Class<?> defineClass(String name, byte[] b) {
return defineClass(name, b, 0, b.length);
}
}
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
/*Instantiate the ASM ClassWriter*/
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
/*Initialize the writer with the specifications*/
classWriter.visit(Opcodes.V1_7, // Java 1.7
Opcodes.ACC_PUBLIC, // public class
"com/Executor/CustomClass", // package and name
null, // signature (null means not generic)
"java/lang/Object", // superclass
new String[]{ "com/Executor/Calculator" }); // interfaces (in this case Calculator interface inside com/Executor Package)
/* Build constructor */
MethodVisitor methodVisitor = classWriter.visitMethod(
Opcodes.ACC_PUBLIC, // public method
"<init>", // method name
"()V", // descriptor
null, // signature (null means not generic)
null); // exceptions (array of strings)
methodVisitor.visitCode(); // Start the code for this method
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0); // Load "this" onto the stack
methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, // Invoke an instance method (non-virtual)
"java/lang/Object", // Class on which the method is defined
"<init>", // Name of the method
"()V"); // Descriptor;
methodVisitor.visitInsn(Opcodes.RETURN); // End the constructor method
methodVisitor.visitMaxs(1, 1); // Specify max stack and local vars
/* Build 'add' method (Implementing Interface method)*/
MethodVisitor mv = classWriter.visitMethod(
Opcodes.ACC_PUBLIC, // public method
"add", // name
"(II)I", // descriptor
null, // signature (null means not generic)
null); // exceptions (array of strings)
mv.visitCode();
mv.visitVarInsn(Opcodes.ILOAD, 1); // Load int value onto stack
mv.visitVarInsn(Opcodes.ILOAD, 2); // Load int value onto stack
mv.visitInsn(Opcodes.IADD); // Integer add from stack and push to stack
mv.visitInsn(Opcodes.IRETURN); // Return integer from top of stack
mv.visitMaxs(2, 3); // Specify max stack and local vars
classWriter.visitEnd(); // Finish the class definition
DynamicClassLoader loader = new DynamicClassLoader();
Class<?> clazz = loader.defineClass("com.Executor.CustomClass", classWriter.toByteArray());
System.out.println(clazz.getName());
Calculator calc = (Calculator)clazz.newInstance();
System.out.println("2 + 2 = " + calc.add(2, 2));
}
}
Выход
com.Executor.CustomClass 2 + 2 = 4