ASM
ASM 是什么?
ASM 是一个十分强大的字节码处理框架,基本上可以实现对字节码的任何操作,但是需要对 Java 字节码有一定了解,ASM 可以通过操作指令码来生成字节码或者插桩,利用 ASM 的 API 来操控字节码时,就有很大的自由度来进行各种字节码的生成、修改、操作等等。ASM 只负责操作字节码,我们还需要通过自定义
gradle plugin
的形式来干预编译过程,在编译过程中获取到所有的class
文件和jar
包,然后遍历他们,利用ASM
来修改字节码,达到插桩的目的。
Java 文件通过 javac
编译后会生成十六进制的 class
字节码文件,这一堆十六进制数是按严格的结构拼接在一起的,按顺序分别是:魔数(cafe babe)、java 版本号、常量池、访问权限标志、当前类索引、父类索引、接口索引、字段表、方法表、附加属性等十个部分,这些部分以十六进制的形式表达出来并紧凑的拼接在一起。这个十六进制文件显然不具备可阅读性,所以我们可以通过 javap -verbose Test
来反编译,有兴趣的可以自己试一试。借助 010 Editor
,可以帮助我们较为清晰的看到 class 文件每一部分的结构,直接拖动你的 class
文件到 010 Editor
即可查看。
// 编译成 class 文件
javac Hello.java
ls -l *.class
rm *.class
// 反编译 class 文件
javap Hello.class
// 查看字节码
javap -verbose Hello.class
// Run Hello.java
java Hello
类型描述符
方法描述符
(参数类型描述符)返回值描述符
// 例如:
boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id)
(Landroid/widget/ExpandableListView;Landroid/view/View;IJ)Z
方法参数列表对应的方法签名如下:
字节码 invokespecial
, invokevirtual
, invokeinterface
, invokestatic
, invokedynamic
有何区别?
invokespecial
,一般指的是调用 super 方法,构造方法,private 方法等;
调用的都是一些确定调用者的方法。调用一个类的方法,调用者还能有不确定的时候?
有呀,比如重载,是不是能将父类的方法调用转而变成子类的?
类中非 private 成员方法,一般调用指令为invokevirtual
invokeinterface
,invokestatic
字面意思理解就可以了
思考:抽象类抽象方法调用和接口方法调用指令一样吗?加了final修饰的方法不能被复写,指令会有变化吗?
ASM 可以直接生成 .class
文件,也可以在该类被 java 虚拟机加载之前,动态改变现有类的行为。
ASM 涉及了五个比较核心的类:
-
ClassReader
-
ClassWriter
主要用来重新构建编译后的类,比如修改类名,属性以及方法,甚至可以生产新的类名字节码。
- MethodVisitor
主要是控制方法访问,它有几个重要的方法:
onMethodEnter()
,visitEnd()
和visitAnnotation()
。onMethodEnter()
主要是进入方法时会调用,visitEnd()
主要是扫描器完成类扫描时才会调用,visitAnnotation()
可以在这里通过注解的方式操作字节码。
- ClassVistor
负责 "拜访" 类成员信息 其中包括类的注解,类的构造方法,类的字段,类的方法,静态代码块,重点需要了解的有:
visit()
和visitMehtod()
- AdviceAdapter
ASM 原理
-
定义一个
Gradle Plugin
,然后注册一个Transform
对象,在transform()
方法里分别遍历 目录 和jar
包 -
遍历当前应用程序所有的
.class
文件找到满足特定条件的.class
文件和相关方法 -
修改相应方法以动态插入字节码