ASM简单入门笔记

栏目: Java · 发布时间: 5年前

内容简介:编写测试类 运行原User 类生成的 .class 文件,以及输出效果字节码修改后的 User 类的 .class 文件以及输出效果
前几天,在Q群里有个大佬,展示了下 Android 做无痕埋点,觉得挺厉害的
问了下使用的是 AspectJ, 网上搜了下资料 ASM  AspectJ 更灵活,更轻量
所以准备学习下
复制代码

2. 介绍

ASM 是一款轻量级的 Java 字节码操作仓库
复制代码

3. 前期准备

3.1 简单的asm 方面的知识

ASM 主要有几个类需要了解 而且需要对 Java字节码 比较熟悉

ClassReader
    字节码的读取与分析引擎。它采用类似SAX的事件读取机制,每当有事件发生时,调用注册的ClassVisitorAnnotationVisitorFieldVisitorMethodVisitor做相应的处理。

ClassVisitor
    定义在读取Class字节码时会触发的事件,如类头解析完成、注解解析、字段解析、方法解析等

AnnotationVisitor
    定义在解析注解时会触发的事件,如解析到一个基本值类型的注解、enum值类型的注解、Array值类型的注解、注解值类型的注解等

FieldVisitor
    定义在解析字段时触发的事件,如解析到字段上的注解、解析到字段相关的属性等
    
MethodVisitor
    定义在解析方法时触发的事件,如方法上的注解、属性、代码等。

ClassWriter
    它实现了ClassVisitor接口,用于拼接字节码。
复制代码

3.2 开发 工具 准备

idea / Android studio 
ASM Bytecode Viewer(对 Java字节码 不熟悉的话必备)
复制代码

4 实战

4.1 要实现的效果

class User {
    public static void main(String[] args) {
        show();
    }

    public static void show(){
        System.out.println("Hello World");
    }
}
复制代码
上图为一个 User类,要对 show() 方法,的耗时进行计算并打印
复制代码

4.2 编写 ASM 逻辑

4.2.1 编写 ClassVisitor

解析类的监听器,解析Class字节码时会触发内部的方法
复制代码
public class TestClassVisitor extends ClassVisitor {
    public TestClassVisitor(final ClassVisitor cv) {
        super(Opcodes.ASM5, cv);
    }

    @Override
    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
        if (cv != null) {
            cv.visit(version, access, name, signature, superName, interfaces);
        }
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
        //如果methodName是show,则返回我们自定义的TestMethodVisitor
        if ("show".equals(name)) {
            MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);
            return new TestMethodVisitor(mv);
        }
        if (cv != null) {
            return cv.visitMethod(access, name, desc, signature, exceptions);
        }
        return null;
    }
}
复制代码

4.2.2 编写 MethodVisitor

解析方法的监听器,解析Method时会触发内部的方法
编写前若对 Java字节码 不熟悉 建议安装 ASM Bytecode Viewer 插件

先新建一个类 编写要注入的代码,然后用插件查看
复制代码
ASM简单入门笔记
public class TestMethodVisitor extends MethodVisitor implements Opcodes {
    public TestMethodVisitor(MethodVisitor mv) {
        super(Opcodes.ASM5, mv);
    }

    @Override
    public void visitCode() {
        //方法体内开始时调用
        mv.visitMethodInsn(INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false);
        mv.visitVarInsn(LSTORE, 0);
        super.visitCode();
    }
    @Override
    public void visitInsn(int opcode) {
        //每执行一个指令都会调用
        if (opcode == Opcodes.RETURN) {
            mv.visitMethodInsn(INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false);
            mv.visitVarInsn(LLOAD, 0);
            mv.visitInsn(LSUB);
            mv.visitVarInsn(LSTORE, 2);
            Label l3 = new Label();
            mv.visitLabel(l3);
            mv.visitLineNumber(11, l3);
            mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
            mv.visitTypeInsn(NEW, "java/lang/StringBuilder");
            mv.visitInsn(DUP);
            mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V", false);
            mv.visitLdcInsn("== method cost time = ");
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
            mv.visitVarInsn(LLOAD, 2);
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(J)Ljava/lang/StringBuilder;", false);
            mv.visitLdcInsn(" ==");
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false);
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);

        }
        super.visitInsn(opcode);
    }
}
复制代码

4.3 测试效果

编写测试类 运行

public class Demo {
    public static void main(String[] args) throws IOException {
        ClassReader cr = new ClassReader(User.class.getName());
        ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS);
        ClassVisitor cv = new TestClassVisitor(cw);
        cr.accept(cv, Opcodes.ASM5);
        // 获取生成的class文件对应的二进制流
        byte[] code = cw.toByteArray();
        //将二进制流写到out/下
        FileOutputStream fos = new FileOutputStream("out/User.class");
        fos.write(code);
        fos.close();
    }
}
复制代码

原User 类生成的 .class 文件,以及输出效果

ASM简单入门笔记
ASM简单入门笔记

字节码修改后的 User 类的 .class 文件以及输出效果

ASM简单入门笔记
ASM简单入门笔记

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

蚂蚁金服

蚂蚁金服

由曦 / 中信出版集团股份有限公司 / 2017-4-7 / CNY 59.00

在中国,支付宝(其母公司为蚂蚁金服)是一个家喻户晓的品牌。我们在用手机扫码支付,或者用余额宝理财的时候,一定会和支付宝发生关系。但是很多人不知道,支付宝的母公司叫作“蚂蚁金服”。蚂蚁金服不仅有支付宝,还有余额宝、网商银行、芝麻信用等一系列产品和服务。成立于2004年、起始于支付宝的蚂蚁金服集团,如今已经是全球估值最高的科技金融企业。然而,在成立之初,它只是淘宝网的结算部门,员工只有区区几人,记账用......一起来看看 《蚂蚁金服》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具