前言
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("