天天看点

[转]ASM插入代码 visitFieldInsn

今天介绍下asm3.0,开始之前先思考几个问题:

1.asm是什么? 2.asm 跟传说中的aop三剑客apt、aspectj、javassit有什么关系? 3.asm是怎样修改class文件的?

带着问题开始今天的分享:

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

分别解释下这几个名词

apt:apt(annotation processing tool)即注解处理器,是一种处理注解的工具,确切的说它是javac的一个工具,它用来在编译时扫描和处理注解。注解处理器以java代码(或者编译过的字节码)作为输入,生成.java文件作为输出。简单来说就是在编译期,通过注解生成.java文件 aspectj:aspectj是一个面向切面的框架,它扩展了java语言。aspectj定义了aop语法,所以它有一个专门的[编译器]用来生成遵守java字节编码规范的class文件。适合在某一个方法前后插入部分代码,处理某些逻辑:比如方法运行时间、插入动态权限检查等。问题会造成很多的冗余代码,产生很多代理类。简单来说就是在生成class时动态织入代码 javassit: javassist是一个开源的分析、编辑和创建java字节码的类库。是由东京工业大学的数学和计算机科学系的 shigeru chiba(千叶滋)所创建的。简单来说就是源码级别的api去修改字节码
[转]ASM插入代码 visitFieldInsn

各种方式作用时机

开始这个问题之前我们先学习几个东西。

字节码

访问者模式

一个称为元素(element),另一个称为访问者(visitor)。元素有一个accept方法,该方法接收访问者作为参数;accept()方法调用访问者的visit()方法,并且将元素自身作为参数传递给访问者。由元素本身决定是否访问

在asm中元素(被访问者)classreader、methodnode等等,访问者接口包含classvisitor、annotationvisitor、fieldvisitor、methodvisitor

下面我们先简单实现一个插桩操作

有如下代码:

我们要在sendmessage方法中添加一行代码变为下列

1.我们这里使用一个android studio的plugin (​<code>​asm bytecode outline​</code>​)查看​<code>​main2activity​</code>​的asm代码,看主要的​<code>​sendmessage​</code>​部分

看起来很懵逼,其实这里只不过是asm帮助我们调用java bytecode罢了。

修改你想要的代码,同样使用<code>asm bytecode outline</code>plugin对比差异代码

看的出我们在​<code>​mv.visitfieldinsn(putfield, "android/os/message", "what", "i");​</code>​后边插入了三行代码(其中的label以及行数设置可以不用理),那么这三行代码什么意思呢?这里就用到了上边提到的java bytecode知识,意思是:将变量2,1分别入栈,并将2变量赋值给message的obj。

好了那么我们开始写asm,这里我们使用android transform api作为前置条件扫描文件。(有关于transform api后期分享)

开始之前先解释几个类:

1.opcodes接口定义了一些常量,尤其是版本号,访问标示符,字节码等信息; 2.classreader用于读取class文件,主要用于class文件的分析,可接受一个classvisitor;classreader会将解析过程中产生的类的部分信息,比如访问标识符,字段,方法逐个送入classvisitor,后者在接收到对应的信息后,进行各自的处理; 3.classvisitor的子类classwriter: 负责进行class文件的输出和生成。classvisitor在进行字段和方法处理的时候,会委托给fieldvistor和methodvisitor进行处理;在类的处理过程中,会创建对应的fieldvisitor和methodvisitor对象;fieldvisitor和methodvisitor类也各自有1个重要的子类,fieldwriter和methodwriter;当classwriter进行字段和方法的处理时,也是依赖这两个类进行的; 4.classvisitor,fieldvisitor,methodvisitor都可以使用委托的方式,将实际的处理工作交给内部的委托类进行;它们内部有一些列的visitxxx方法,这些方法就是asm 的实际方法code。

创建元素跟访问者

这里的访问者​<code>​scanclassvisitor​</code>​继承了​<code>​classvisitor​</code>​,这里我们要修改某一个方法,所以实现了​<code>​visitmethod​</code>​方法,并筛选其中的​<code>​sendmessage​</code>​方法

上边的​<code>​scanmethodvisitor​</code>​是实现了​<code>​methodvisitor​</code>​访问者,看asm代码,我们需要在​<code>​mv.visitfieldinsn(putfield, "android/os/message", "what", "i");​</code>​行后插入代码,所以需要实现​<code>​visitfieldinsn​</code>​方法

opcode: putfield owner: "android/os/message" name:"what" desc:"i"

好了接下来运行代码transform看效果

总结:asm直接修改class文件确实效率很高,但因直接操作字节码,需要有字节码知识,不适合直接上手,相比较来​<code>​javassit​</code>​源码级修改class文件更方便些。

demo

作者:heiheiwanne