在開發中一般情況下我們寫好的代碼然後編譯成class文件並運行屬於靜態的class文件生成,那是不是class文件就只有靜態生成一種啊,其實不然,在jdk的動態代理應用Proxy類就是已經使用了動態生成一個class文件來實現代理功能的,只是這一部分我們都看不到,而asm是一個專門的字節碼動態生成的一個框架,其架構使用的是生產、消費和過濾模式對應的接口和類分別是ClassReader讀取一個Class文件屬於生產模式,classWriter是生產一個class文件屬於消費模式,ClassAdapter是對ClassWriter的過濾,做修改功能的,classReader接受一個ClassWriter並在方法中調用ClassWriter的所有的方法來實現生產並消費。當我們修改一個類的時候就必須繼承ClassAdapter利用ClassAdapter中包含的ClassWriter對象對class文件進行修改。如果只是動態生成一個class就直接使用classWriter和CodeWriter就可以了。運用asm進行動態生成和修改一個class文靜必須瞭解class文件的結構和一些指令。但是CGLib(Code Generator Library代碼生成庫)幫我們做了封裝和擴展讓我們更容易去實現這個功能,不用瞭解class文件的結構也可以做到。其中CGLib的類的動態代理就是利用asm字節碼動態生成而實現的,在這裏說一下CGLib只是對asm的封裝和擴展,CGLib的對class文件及一個類的操作是基於asm的歡聚換說asm是CGLib的底層,關於asm可以參考asm使用手冊。網上是可以下載的,有興趣的話可以研究一下。下面給出一些實際操作的小例子:
public void generator()
{//自動生成一個class文件
ClassWriter cw=new ClassWriter(0);
cw.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC+Opcodes.ACC_ABSTRACT, "com/test/bean/Base", null,"com/test/bean/Test", new String[]{"com/test/bean/IDAO"});
cw.visitField(Opcodes.ACC_PUBLIC+Opcodes.ACC_STATIC+Opcodes.ACC_FINAL, "name", "Ljava/lang/String;",null,"accp" ).visitEnd();
cw.visitField(Opcodes.ACC_PUBLIC+Opcodes.ACC_STATIC+Opcodes.ACC_FINAL, "age", "I",null, new Integer(20)).visitEnd();
cw.visitMethod(Opcodes.ACC_PUBLIC+Opcodes.ACC_ABSTRACT,"hello", "()V", null,null).visitEnd();
cw.visitMethod(Opcodes.ACC_PUBLIC+Opcodes.ACC_ABSTRACT,"add", "(Ljava/lang/Object;)V", null,null).visitEnd();
cw.visitMethod(Opcodes.ACC_PUBLIC+Opcodes.ACC_ABSTRACT,"update","(Ljava/lang/Object;)V",null,null).visitEnd();
cw.visitMethod(Opcodes.ACC_PUBLIC+Opcodes.ACC_ABSTRACT,"delete","(Ljava/lang/Object;)V", null,null).visitEnd();
cw.visitEnd();
byte[] b=cw.toByteArray();//構建好一個class文件
ClassReader cr=new ClassReader(b);
ClassWriter cw2=new ClassWriter(0);
//給class文件添加一個方法
cr.accept(new AddMethodAdapter(cw2,Opcodes.ACC_PUBLIC+Opcodes.ACC_ABSTRACT,"get","(Ljava/lang/String;)Ljava/lang/Object;"), 0);
//從class文件中移除一個方法
//cr.accept(new RemoveMethodAdapter(cw2,"delete","(Ljava/lang/Object;)V"), false);
b=cw2.toByteArray();//生成新的class文件
try {
FileOutputStream out=new FileOutputStream(new File("D:Base.class"));
out.write(b);
out.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 移除方法過濾器
* @author liurm
*
*/
public class RemoveMethodAdapter extends ClassAdapter {
private String name;//方法名稱
private String desc;//方法描述
public RemoveMethodAdapter(ClassVisitor classvisitor,String name,String desc) {
super(classvisitor);
// TODO Auto-generated constructor stub
this.name=name;
this.desc=desc;
}
public MethodVisitor visitMethod(int access, String name, String desc,
String signature, String[] exceptions) {
// TODO Auto-generated method stub
if(name.equals(this.name)&&desc.equals(this.desc))
{
return null;//移除方法
}
return super.visitMethod(access, name, desc, signature, exceptions);
}
}
/*
* 添加方法過濾器
* */
public class AddMethodAdapter extends ClassAdapter{
private int acc;//訪問修飾符
private String name;//方法名稱
private String desc;//方法描述
private boolean isMethodPersent;//是否已經添加
public AddMethodAdapter(ClassVisitor classvisitor,int acc,String name,String desc) {
super(classvisitor);
// TODO Auto-generated constructor stub
this.acc=acc;
this.name=name;
this.desc=desc;
}
public MethodVisitor visitMethod(int access, String name, String desc,
String signature, String[] exceptions) {
// TODO Auto-generated method stub
if(name.equals(this.name)&&desc.equals(this.desc))
{//方法已經添加
isMethodPersent=true;
}
return super.visitMethod(access, name, desc, signature, exceptions);
}
public void visitEnd() {
// TODO Auto-generated method stub
if(!isMethodPersent)
{//未添加是才添加
super.cv.visitMethod(acc, name, desc, null, null);
}
super.visitEnd();
}
}
class PrintClass implements org.objectweb.asm.ClassVisitor{
public void visitAttribute(Attribute attribute) {
// TODO Auto-generated method stub
}
public void visitEnd() {
// TODO Auto-generated method stub
System.out.println("}");
}
public void visitInnerClass(String name, String outername, String innername, int access) {
// TODO Auto-generated method stub
}
public void visit(int version, int access, String name, String signutre,
String supername, String[] interfacename) {
// TODO Auto-generated method stub
System.out.println(name+" extends "+supername+"{");
}
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
// TODO Auto-generated method stub
return null;
}
public FieldVisitor visitField(int access, String name, String desc,
String signature, Object value) {
// TODO Auto-generated method stub
System.out.println(desc+" "+name);
return null;
}
public MethodVisitor visitMethod(int access, String name, String desc,
String signature, String[] exceptions) {
// TODO Auto-generated method stub
System.out.println(name+desc);
return null;
}
public void visitOuterClass(String owner, String name, String desc) {
// TODO Auto-generated method stub
}
public void visitSource(String arg0, String arg1) {
// TODO Auto-generated method stub
}
}
生成Get,Set方法,一個普通的java類
ClassWriter cw=new ClassWriter(0);//手動計算內存
cw.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC,"com/test/bean/Student",null,"java/lang/Object",null);//聲明一個類
cw.visitField(Opcodes.ACC_PRIVATE, "age", "I", null,new Integer(20)).visitEnd();//定義age字段
cw.visitField(Opcodes.ACC_PRIVATE, "name", "Ljava/lang/String;",null,"accp").visitEnd();//定義name字段
//構造函數
MethodVisitor mv4=cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
mv4.visitCode();
mv4.visitVarInsn(Opcodes.ALOAD, 0);
mv4.visitMethodInsn(Opcodes.INVOKESPECIAL, "com/test/bean/Student", "<init>", "()V");
mv4.visitInsn(Opcodes.RETURN);
mv4.visitMaxs(1, 1);
mv4.visitEnd();
//構建getAge方法
MethodVisitor mv=cw.visitMethod(Opcodes.ACC_PUBLIC, "getAge", "()I", null,null);
mv.visitCode();
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitFieldInsn(Opcodes.GETFIELD, "com/test/bean/Student", "age", "I");
mv.visitInsn(Opcodes.IRETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
//構建setAge方法
MethodVisitor mv1=cw.visitMethod(Opcodes.ACC_PUBLIC, "setAge", "(I)V", null, null);
mv1.visitCode();
mv1.visitVarInsn(Opcodes.ALOAD, 0);
mv1.visitVarInsn(Opcodes.ALOAD, 1);
mv1.visitFieldInsn(Opcodes.PUTFIELD, "com/test/bean/Student", "age", "I");
mv1.visitInsn(Opcodes.RETURN);
mv1.visitMaxs(2, 2);
mv1.visitEnd();
//構建getName方法
MethodVisitor mv2=cw.visitMethod(Opcodes.ACC_PUBLIC, "getName", "()Ljava/lang/String;", null,null);
mv2.visitCode();
mv2.visitVarInsn(Opcodes.ALOAD, 0);
mv2.visitFieldInsn(Opcodes.GETFIELD, "com/test/bean/Student", "name", "Ljava/lang/String;");
mv2.visitInsn(Opcodes.ARETURN);
mv2.visitMaxs(1, 1);
mv2.visitEnd();
//構建setName方法
MethodVisitor mv3=cw.visitMethod(Opcodes.ACC_PUBLIC, "setName", "(Ljava/lang/String;)V", null, null);
mv3.visitCode();
mv3.visitVarInsn(Opcodes.ALOAD, 0);
mv3.visitVarInsn(Opcodes.ALOAD, 1);
mv3.visitFieldInsn(Opcodes.PUTFIELD, "com/test/bean/Student", "name", "Ljava/lang/String;");
mv3.visitInsn(Opcodes.RETURN);
mv3.visitMaxs(2, 2);
mv3.visitEnd();
//sayhello方法
MethodVisitor mv5=cw.visitMethod(Opcodes.ACC_PUBLIC, "sayhello", "(Ljava/lang/String;)Ljava/lang/String;", null, null);
mv5.visitCode();
mv5.visitTypeInsn(Opcodes.NEW, "java/lang/StringBuilder");
mv5.visitInsn(Opcodes.DUP);
mv5.visitLdcInsn("Hello:");
mv5.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "(Ljava/lang/String;)V");
mv5.visitVarInsn(Opcodes.ALOAD, 1);
mv5.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;");
mv5.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;");
mv5.visitInsn(Opcodes.ARETURN);
mv5.visitMaxs(3, 2);
mv5.visitEnd();
//靜態塊
MethodVisitor mv6=cw.visitMethod(Opcodes.ACC_STATIC, "<clinit>","()V", null,null);
mv6.visitCode();
//System.out.println("Hello World!");
mv6.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
mv6.visitLdcInsn("Hello World!");
mv6.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V");
//創建數組int [] a=new int[5];
//mv6.visitInsn(Opcodes.ICONST_5);
//int [] a=new int[6];
mv6.visitLdcInsn(new Integer(6));
//mv6.visitIntInsn(Opcodes.NEWARRAY, Opcodes.T_INT);
mv6.visitVarInsn(Opcodes.NEWARRAY, Opcodes.T_INT);
mv6.visitVarInsn(Opcodes.ASTORE, 4);
mv6.visitInsn(Opcodes.RETURN);
mv6.visitMaxs(4,0);
mv6.visitEnd();
cw.visitEnd();
byte[] b=cw.toByteArray();//可以裝載到JVM中了