java中如何使用asm動態的生成或修改一個class文件以及asm的架構思想

在開發中一般情況下我們寫好的代碼然後編譯成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中了

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章