zoukankan      html  css  js  c++  java
  • 关于java字节码框架ASM的学习

     

     

      一、什么是ASM

      ASM是一个java字节码操纵框架它能被用来动态生成类或者增强既有类的功能ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。Java class 被存储在严格格式定义的 .class文件里,这些类文件拥有足够的元数据来解析类中的所有元素:类名称、方法、属性以及 Java 字节码(指令)。ASM从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。

      使用ASM框架需要导入asm的jar包,下载链接asm-3.2.jar

      二、如何使用ASM

      ASM框架中的核心类有以下几个:

      ①  ClassReader:该类用来解析编译过的class字节码文件。

      ②  ClassWriter:该类用来重新构建编译后的类,比如说修改类名、属性以及方法,甚至可以生成新的类的字节码文件。

      ③  ClassAdapter:该类也实现了ClassVisitor接口,它将对它的方法调用委托给另一个ClassVisitor对象

      示例1.通过asm生成类的字节码

    复制代码
     1 package com.asm3;
     2 
     3 import java.io.File;
     4 import java.io.FileNotFoundException;
     5 import java.io.FileOutputStream;
     6 import java.io.IOException;
     7 
     8 import org.objectweb.asm.ClassWriter;
     9 import org.objectweb.asm.Opcodes;
    10 
    11 /**
    12  * 通过asm生成类的字节码
    13  * @author Administrator
    14  *
    15  */
    16 public class GeneratorClass {
    17 
    18     public static void main(String[] args) throws IOException {
    19         //生成一个类只需要ClassWriter组件即可
    20         ClassWriter cw = new ClassWriter(0);
    21         //通过visit方法确定类的头部信息
    22         cw.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC+Opcodes.ACC_ABSTRACT+Opcodes.ACC_INTERFACE,
    23                 "com/asm3/Comparable", null, "java/lang/Object", new String[]{"com/asm3/Mesurable"});
    24         //定义类的属性
    25         cw.visitField(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL+Opcodes.ACC_STATIC,
    26                 "LESS", "I", null, new Integer(-1)).visitEnd();
    27         cw.visitField(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL+Opcodes.ACC_STATIC,
    28                 "EQUAL", "I", null, new Integer(0)).visitEnd();
    29         cw.visitField(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL+Opcodes.ACC_STATIC,
    30                 "GREATER", "I", null, new Integer(1)).visitEnd();
    31         //定义类的方法
    32         cw.visitMethod(Opcodes.ACC_PUBLIC+Opcodes.ACC_ABSTRACT, "compareTo",
    33                 "(Ljava/lang/Object;)I", null, null).visitEnd();
    34         cw.visitEnd(); //使cw类已经完成
    35         //将cw转换成字节数组写到文件里面去
    36         byte[] data = cw.toByteArray();
    37         File file = new File("D://Comparable.class");
    38         FileOutputStream fout = new FileOutputStream(file);
    39         fout.write(data);
    40         fout.close();
    41     }
    42 }
    复制代码

      生成一个类的字节码文件只需要用到ClassWriter类即可,生成Comparable.class后用javap指令对其进行反编译:javap -c Comparable.class >test.txt  ,编译后的结果如下:

    复制代码
    1 public interface com.asm3.Comparable extends com.asm3.Mesurable {
    2   public static final int LESS;
    3 
    4   public static final int EQUAL;
    5 
    6   public static final int GREATER;
    7 
    8   public abstract int compareTo(java.lang.Object);
    9 }
    复制代码

      注:一个编译后的java类不包含package和import段,因此在class文件中所有的类型都使用的是全路径。

      示例2.修改类的字节码文件

    C.java

    复制代码
    1 package com.asm5;
    2 
    3 public class C {
    4     public void m() throws InterruptedException{
    5         Thread.sleep(100); 
    6     }
    7 }
    复制代码

    将C.java类的内容改为如下:

    复制代码
     1 package com.asm5;
     2 
     3 public class C {
     4     public static long timer;
     5     public void m() throws InterruptedException{
     6         timer -= System.currentTimeMillis();
     7         Thread.sleep(100); 
     8         timer += System.currentTimeMillis();
     9     }
    10 }
    复制代码

      为了弄清楚ASM是如何实现的,我们先编译这两个类,然后比对它们的TraceClassVisitor的输出,我们可以发现如下的不同(粗体表示)

    GETSTATIC C.timer : J
    INVOKESTATIC java/lang/System.currentTimilis()J
    LSUB
    PUTSTATIC C.timer : J
    LDC 100
    INVOKESTATIC java/lang/Thread.sleep(J)V
    GETSTATIC C.timer : J
    INVOKESTATIC java/lang/System.currentTimilis()J
    LADD
    PUTSTATIC C.timer : J
    RETURN
    MAXSTACK=4
    MAXLOCALS=1

      通过比对上面的指令,我们可以发现必须在m()方法的最前面增加四条指令,在RETURN指令前也增加四条指令,同时这四条必须位于xRETURN和ATHROW之前,因为这些指令都会结束方法的执行。

    具体代码如下:

    AddTimeClassAdapter.java

    复制代码
     1 package com.asm5;
     2 
     3 import org.objectweb.asm.ClassAdapter;
     4 import org.objectweb.asm.ClassVisitor;
     5 import org.objectweb.asm.FieldVisitor;
     6 import org.objectweb.asm.MethodAdapter;
     7 import org.objectweb.asm.MethodVisitor;
     8 import org.objectweb.asm.Opcodes;
     9 
    10 public class AddTimeClassAdapter extends ClassAdapter {
    11     private String owner;
    12     private boolean isInterface;
    13     public AddTimeClassAdapter(ClassVisitor cv) {
    14         super(cv);
    15     }
    16     @Override
    17     public void visit(int version, int access, String name, String signature,
    18             String superName, String[] interfaces) {
    19         cv.visit(version, access, name, signature, superName, interfaces);
    20         owner = name;
    21         isInterface = (access & Opcodes.ACC_INTERFACE) != 0;
    22     }
    23     @Override
    24     public MethodVisitor visitMethod(int access, String name, String desc,
    25             String signature, String[] exceptions) {
    26         MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);
    27         if(!name.equals("<init>") && !isInterface && mv!=null){
    28             //为方法添加计时功能
    29             mv = new AddTimeMethodAdapter(mv);
    30         }
    31         return mv;
    32     }
    33     @Override
    34     public void visitEnd() {
    35         //添加字段
    36         if(!isInterface){
    37             FieldVisitor fv = cv.visitField(Opcodes.ACC_PUBLIC+Opcodes.ACC_STATIC, "timer", "J", null, null);
    38             if(fv!=null){
    39                 fv.visitEnd();
    40             }
    41         }
    42         cv.visitEnd();
    43     }
    44     
    45     class AddTimeMethodAdapter extends MethodAdapter{
    46         public AddTimeMethodAdapter(MethodVisitor mv) {
    47             super(mv);
    48         }
    49         @Override
    50         public void visitCode() {
    51             mv.visitCode();
    52             mv.visitFieldInsn(Opcodes.GETSTATIC, owner, "timer", "J");
    53             mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J");
    54             mv.visitInsn(Opcodes.LSUB);
    55             mv.visitFieldInsn(Opcodes.PUTSTATIC, owner, "timer", "J");
    56         }
    57         @Override
    58         public void visitInsn(int opcode) {
    59             if((opcode>=Opcodes.IRETURN && opcode<=Opcodes.RETURN) || opcode==Opcodes.ATHROW){
    60                 mv.visitFieldInsn(Opcodes.GETSTATIC, owner, "timer", "J");
    61                 mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J");
    62                 mv.visitInsn(Opcodes.LADD);
    63                 mv.visitFieldInsn(Opcodes.PUTSTATIC, owner, "timer", "J");
    64             }
    65             mv.visitInsn(opcode);
    66         }
    67         @Override
    68         public void visitMaxs(int maxStack, int maxLocal) {
    69             mv.visitMaxs(maxStack+4, maxLocal);
    70         }
    71     }
    72     
    73 }
    复制代码

    Generator.java

    复制代码
     1 package com.asm5;
     2 
     3 import java.io.File;
     4 import java.io.FileNotFoundException;
     5 import java.io.FileOutputStream;
     6 import java.io.IOException;
     7 
     8 import org.objectweb.asm.ClassAdapter;
     9 import org.objectweb.asm.ClassReader;
    10 import org.objectweb.asm.ClassWriter;
    11 
    12 
    13 
    14 public class Generator {
    15 
    16     public static void main(String[] args){
    17         try {
    18             ClassReader cr = new ClassReader("com/asm5/C");
    19             ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
    20             ClassAdapter classAdapter = new AddTimeClassAdapter(cw);
    21             //使给定的访问者访问Java类的ClassReader
    22             cr.accept(classAdapter, ClassReader.SKIP_DEBUG);
    23             byte[] data = cw.toByteArray();
    24             File file = new File(System.getProperty("user.dir") + "\WebRoot\WEB-INF\classes\com\asm5\C.class");
    25             FileOutputStream fout = new FileOutputStream(file);
    26             fout.write(data);
    27             fout.close();
    28             System.out.println("success!");
    29         } catch (FileNotFoundException e) {
    30             e.printStackTrace();
    31         } catch (IOException e) {
    32             e.printStackTrace();
    33         }
    34     }
    35 
    36 }
    复制代码

    下面是一个测试类:

    复制代码
     1 package com.asm5;
     2 
     3 public class Test {
     4     public static void main(String[] args) throws InterruptedException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
     5         C c = new C();
     6         c.m();
     7         Class cc = c.getClass();
     8         System.out.println(cc.getField("timer").get(c));
     9     }
    10 }
    复制代码

    输出结果为:100

  • 相关阅读:
    Windows JScript 在 游览器 中运行 调试 Shell 文件系统
    autohotkey 符号链接 软连接 symbolink
    软链接 硬链接 测试
    SolidWorks 修改 基准面 标准坐标系
    手机 路径 WebDAV 映射 驱动器
    Win10上手机路径
    explorer 命令行
    单位公司 网络 封锁 屏蔽 深信 AC
    cobbler自动化部署原理篇
    Docker四种网络模式
  • 原文地址:https://www.cnblogs.com/shoshana-kong/p/10560756.html
Copyright © 2011-2022 走看看