Java ASM 框架:字节码操作的常见用法(生成类,修改类,方法插桩,方法注入)

前言

ASM 是一款读写Java字节码的工具,可以达到跳过源码编写,编译,直接以字节码的形式创建类,修改已经存在类(或者jar中的class)的属性,方法等。 通常用来开发一些Java开发的辅助框架,其做法是在你编写的Java代码中注入一些特定代码(俗称字节码插装)达到特定目的,以Android开发为例最常用的方法通过字节码插装实现热修复,事件监听,埋点,开源框架等非常规操作,当然在Android开发中通常辅以Gradle插件一起使用,这个改天在写。

背景

早就听说过ASM和字节码插桩技术,但是工作中很少直接使用,因为近期有这个学习需求,特做此笔记,供有需要的同学依葫芦画瓢,也作为自己的参考笔记以备后用。

据我实操过程中发现至少有以下三个地方可以获得ASM API的地方

1.ASM官网:http://asm.ow2.io/ 这里有从4.0到最新的9.3 所有版本,你可以下载到响应的jar包,还有使用手册http://asm.ow2.io/asm4-guide.…, 然后依赖到Java工程中即可,其API没有太大的差异。

2.jdk 自带的asm api(我的是JDK11)

3.gradle 自带的api(因为我是Android开发,我用了这种方法:使用的时候只需要添加依赖就行

dependencies {
    implementation gradleApi()

//    testImplementation 'org.ow2.asm:asm:7.1'
//    testImplementation 'org.ow2.asm:asm-commons:7.1'
}

为了更好的参考字节码建议在Android Studio安装 ASM 相关插件,如图所示,安装一下3种中的一种即可,建议安装第三种(最多只能安装1种,否则你的Android studio 下次就无法重启了

四种ASM的常用使用场景,供有需要的同学做参考

1.生成一个完整的类(包含几种基本属性和方法)

假如我们想生成如下类

package com.study;

import java.util.ArrayList;

public class Human {
    private String name;
    private long age;
    protected int no;
    public static long score;
    public static final String real_name = "Sand哥";

    public Human() {
    }

    public int greet(String var1) {
        System.out.println(var1);
        ArrayList var2 = new ArrayList();
        StringBuilder var3 = new StringBuilder();
        var3.append("Hello java asm StringBuilder");
        long var4 = System.nanoTime();
        return 10 + 11;
    }

    public static void staticMethod(String var0) {
        System.out.println("Hello Java Asm!");
    }
}

用Java ASM 该如何生成(必要的地方有详细注释)

public static void testCreateAClass()throws Exception{

        //新建一个类生成器,COMPUTE_FRAMES,COMPUTE_MAXS这2个参数能够让asm自动更新操作数栈
        ClassWriter cw=new ClassWriter(COMPUTE_FRAMES|COMPUTE_MAXS);
        //生成一个public的类,类路径是com.study.Human
        cw.visit(V1_8,ACC_PUBLIC,"com/study/Human",null,"java/lang/Object",null);

        //生成默认的构造方法: public Human()
        MethodVisitor mv=cw.visitMethod(ACC_PUBLIC,"<init>","()V",null,null);
        mv.visitVarInsn(ALOAD,0);
        mv.visitMethodInsn(INVOKESPECIAL,"java/lang/Object","<init>","()V",false);
        mv.visitInsn(RETURN);
        mv.visitMaxs(0,0);//更新操作数栈
        mv.visitEnd();//一定要有visitEnd

        //生成成员变量
        //1.生成String类型的成员变量:private String name;
        FieldVisitor fv= cw.visitField(ACC_PRIVATE,"name","Ljava/lang/String;",null,null);
        fv.visitEnd();//不要忘记end
        //2.生成Long类型成员:private long age
        fv=cw.visitField(ACC_PRIVATE,"age","J",null,null);
        fv.visitEnd();

        //3.生成Int类型成员:protected int no
        fv=cw.visitField(ACC_PROTECTED,"no","I",null,null);
        fv.visitEnd();

        //4.生成静态成员变量:public static long score
        fv=cw.visitField(ACC_PUBLIC+ACC_STATIC,"score","J",null,null);

        //5.生成常量:public static final String real_name = "Sand哥"
        fv=cw.visitField(ACC_PUBLIC+ACC_STATIC+ACC_FINAL,"real_name","Ljava/lang/String;",null,"Sand哥");
        fv.visitEnd();

        //6.生成成员方法greet
        mv=cw.visitMethod(ACC_PUBLIC,"greet","(Ljava/lang/String;)I",null,null);
        mv.visitCode();
        mv.visitIntInsn(ALOAD,0);
        mv.visitIntInsn(ALOAD,1);

        //6.1 调用静态方法 System.out.println("Hello");
        mv.visitFieldInsn(GETSTATIC,"java/lang/System","out","Ljava/io/PrintStream;");
//        mv.visitLdcInsn("
  • 5
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值