加载器:
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 = "<init>";
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 <init> ()"));
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();
}
}