의 내가 추상 클래스가 있다고 가정 해 보자 : 나는 런타임에 확장 할런타임시 추상 메소드 구현?
이abstract class Foo extends Bar {
public abstract int foo();
}
클래스 개체를 만들 수 있습니다. 클래스 객체에 의해 표현 될 것이다
class FooImpl extends Foo {
@Override
public int foo() {
return 5;
}
}
나는 다음의 새로운 인스턴스를 만들 반사를 사용할 수 : 희망은 내가 동적으로 생성 된 클래스를 가질 수있는 것이다. 핵심은 런타임에 foo() 메서드의 반환 값을 결정하고 싶습니다. 내 생각은 ASM을 사용하여 클래스의 바이트 코드를 만든 다음 ClassLoader 객체에서 반향을 사용하여 클래스를 정의하는 것입니다.
ASM을 사용하고 생성 된 바이트에 대해 ClassLoader # defineClass 메소드를 리플렉션하면 하드 코딩되지 않은 값으로 런타임시 추상 메소드를 구현하는 가장 좋은 방법은 무엇입니까?
그렇다면 어떻게해야할까요? 내 직감은 ASMifierClassVisitor를 활용하는 것이지만 정확한 방법은 잘 모르겠습니다. 다른 모든 것이 실패 할 경우 특정 클래스를 정의하는 데 필요한 JVM 명령어를 수동으로 처리 할 수 있지만 더 쉬운 방법이 있어야한다고 생각합니다.
아니요, 가장 좋은 방법은 무엇이며 어떻게하면 가장 좋은 방법을 사용할 수 있습니까?
편집 : 모든 답변을 확인한 결과, 그 중 아무도 내가 원하는 것을 정확히 결정하지 못했습니다. 결국 ASM과 관련하여 소량의 구현을 작성했습니다. 여기에 게시해야한다고 생각했습니다.
import org.objectweb.asm.*;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
/**
* Created by IntelliJ IDEA.
* User: Matt
* Date: 9/17/11
* Time: 12:42 PM
*/
public class OverrideClassAdapter extends ClassAdapter {
private final HashMap<String, Object> code;
private final String className;
private final ClassWriter writer;
private String superName;
public OverrideClassAdapter(ClassWriter writer, String className, Queue<int[]> constructorCode, HashMap<String, Object> code) {
super(writer);
this.writer = writer;
this.className = className;
this.code = code;
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
this.superName = name;
if((access & Opcodes.ACC_ABSTRACT) != 0)
access &= ~Opcodes.ACC_ABSTRACT;
if((access & Opcodes.ACC_INTERFACE) != 0)
access &= ~Opcodes.ACC_INTERFACE;
cv.visit(version, access, className, signature, name, null);
}
@Override
public void visitSource(String source, String debug) {
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
boolean isAbstract = (access & Opcodes.ACC_ABSTRACT) != 0;
if(isAbstract)
access &= ~Opcodes.ACC_ABSTRACT;
MethodWriter mw = (MethodWriter) cv.visitMethod(access, name, desc, signature, exceptions);
Object value = code.get(name);
if(isAbstract || value != null) {
if(value instanceof BytecodeValue) {
BytecodeValue returnableValue = (BytecodeValue) value;
int[] byteCode = new int[returnableValue.getValueCode().length + 1];
System.arraycopy(returnableValue.getValueCode(), 0, byteCode, 0, returnableValue.getValueCode().length);
if(returnableValue.getValueCode().length > 1 && returnableValue.getValueCode()[1] == 0) {
byteCode[1] = writer.newConst(returnableValue.getValue());
}
byteCode[byteCode.length - 1] = returnableValue.getReturnCode();
value = byteCode;
}
return new OverrideMethodAdapter(mw, (int[]) value);
}
return mw;
}
private class OverrideMethodAdapter extends MethodAdapter {
private final int[] code;
private final MethodWriter writer;
public OverrideMethodAdapter(MethodWriter writer, int[] code) {
super(writer);
this.writer = writer;
this.code = code;
}
@Override
public void visitEnd() {
try {
Field code = MethodWriter.class.getDeclaredField("code");
code.setAccessible(true);
ByteVector bytes = new ByteVector();
for(int b : this.code)
bytes.putByte(b);
code.set(writer, bytes);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static byte[] extendClassBytes(Class clazz, String className, HashMap<String, Object> methodImpls) throws IOException {
ClassReader cr = new ClassReader(clazz.getName());
ClassWriter cw = new ClassWriter(0);
cr.accept(new OverrideClassAdapter(cw, className, methodImpls), ClassReader.SKIP_DEBUG);
cr = new ClassReader(cw.toByteArray());
cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
cr.accept(cw, ClassReader.SKIP_DEBUG);
//CheckClassAdapter.verify(new org.objectweb.asm.ClassReader(cw.toByteArray()), true, new PrintWriter(System.out));
/*File file = new File(className + ".class");
new FileOutputStream(file).write(cw.toByteArray());*/
return cw.toByteArray();
}
public static Class extendClass(Class clazz, String className, HashMap<String, Object> methodImpls) throws IOException {
return defineClass(extendClassBytes(clazz, className, methodImpls), className);
}
public static Class defineClass(byte[] code, String name) {
try {
Method method = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
method.setAccessible(true);
return (Class) method.invoke(Thread.currentThread().getContextClassLoader(), name, code, 0, code.length);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
당신은 합성 방법을 봐야 할 것 같아요. – Shahzeb
런타임시'foo'의 반환 값을 결정 하시겠습니까? 클래스가 임의의 타입을 반환하면 어떻게 호출할까요? –
런타임시 foo의 값은 클래스가 생성 될 때 결정되므로 나중에 변경할 필요가 없습니다. – mburke13