JAva asm GeneratorAdapterサブクラスを生成してロード


ローダ:
package com.zz4955.asm.generatoradaptor;

public class MyClassLoader extends ClassLoader {

    public Class defineClass(String name, byte[] b) {
        return defineClass(name, b, 0, b.length);
    }
}

インタフェース:
package com.zz4955.asm.generatoradaptor;

public interface Graph {
    String getShape();
}

サブクラス、javaコードは次のとおりです.後でGeneratorAdapterでこのサブクラスのバイトコードを生成します.
package com.zz4955.asm.generatoradaptor;

public class Circle implements Graph {

    private String shape;

    public Circle(String shape) {
        this.shape = shape;
    }

    @Override
    public String getShape() {
        return shape;
    }
}

GeneratorAdapterは、上のサブクラスを生成します.
package com.zz4955.asm.generatoradaptor;

import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.GeneratorAdapter;
import org.objectweb.asm.commons.Method;

import static org.objectweb.asm.Opcodes.*;
import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
import static org.objectweb.asm.Opcodes.RETURN;

public class GenerateAsmCode {

    public byte[] getCodeByGeneratorAdaptor(String className, String functionName, Class interf) { //     Graph      ,    com.zz4955.asm.generatoradaptor.Circle  。
        String clsLastName = "";
        String varName = "shape";
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); //   :  ClassWriter.COMPUTE_FRAMES,   0!

        //      
        cw.visit(V1_6, ACC_PUBLIC + ACC_SUPER, className.replace('.', '/'), null, "java/lang/Object", new String[] {Type.getType(interf).getInternalName()});
        cw.visitField(ACC_PRIVATE, varName, Type.getDescriptor(String.class), null, null).visitEnd();

        //              
        Method initMethod = new Method(clsLastName, Type.VOID_TYPE, new Type[]{Type.getType(String.class)}); //            
        GeneratorAdapter initAdapter = new GeneratorAdapter(ACC_PUBLIC, initMethod, null, null, cw);
        initAdapter.visitCode();
        initAdapter.loadThis();
        initAdapter.invokeConstructor(Type.getType(Object.class), Method.getMethod("void  ()"));
        initAdapter.loadThis();
        initAdapter.loadArg(0);
        initAdapter.putField(Type.getObjectType(className.replace('.', '/')), varName, Type.getType(String.class));
        initAdapter.returnValue();
        initAdapter.endMethod();
        initAdapter.visitEnd();

        //       
        Method funMethod = new Method(functionName, Type.getType(String.class), new Type[]{});
        GeneratorAdapter funAdaper = new GeneratorAdapter(ACC_PUBLIC, funMethod, null, null, cw);
        funAdaper.visitCode();
        funAdaper.loadThis();
        funAdaper.getField(Type.getObjectType(className.replace('.', '/')), varName, Type.getType(String.class));
        funAdaper.returnValue();
        funAdaper.endMethod();
        funAdaper.visitEnd();
        cw.visitEnd();

        return cw.toByteArray();
    }
}

クライアント:
package com.zz4955.asm.generatoradaptor;

import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class GeneratorAdatperClient {

    public static void main(String[] args) throws Exception {
        MyClassLoader myClassLoader = new MyClassLoader();

        //     
        String graphfilepath = "F:\\asm_ex\\target\\classes\\com\\zz4955\\asm\\generatoradaptor\\Graph.class";
        byte[] codeGraph = inputStream2ByteArray(graphfilepath);
        Class clsGraph = myClassLoader.defineClass("com.zz4955.asm.generatoradaptor.Graph", codeGraph);
        System.out.println(clsGraph.getCanonicalName());

        //       
        GenerateAsmCode generateAsmCode = new GenerateAsmCode();
        String className2 = "com.zz4955.asm.generatoradaptor.Circle";
        String functionName2 = "getShape";
        byte[] code = generateAsmCode.getCodeByGeneratorAdaptor(className2, functionName2, clsGraph);

        writeToFile(code);

        Class cls = myClassLoader.defineClass(className2, code);
        Constructor constructor = cls.getDeclaredConstructor(new Class[]{String.class});
        constructor.setAccessible(true);
        Object obj = constructor.newInstance(new Object[]{"shape is circle"});
        Method method = cls.getMethod(functionName2);
        System.out.println(method.invoke(obj));
    }

    private static void writeToFile(byte[] code) throws Exception {
        OutputStream out = new FileOutputStream("f:/Circle.class");
        InputStream is = new ByteArrayInputStream(code);
        byte[] buff = new byte[1024];
        int len = 0;
        while((len=is.read(buff))!=-1){
            out.write(buff, 0, len);
        }
        is.close();
        out.close();
    }

    private static byte[] inputStream2ByteArray(String filePath) throws IOException {
        InputStream in = new FileInputStream(filePath);
        byte[] data = toByteArray(in);
        in.close();

        return data;
    }

    private static byte[] toByteArray(InputStream in) throws IOException {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024 * 4];
        int n = 0;
        while ((n = in.read(buffer)) != -1) {
            out.write(buffer, 0, n);
        }
        return out.toByteArray();
    }
}