今天介绍下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去修改字节码

各种方式作用时机
开始这个问题之前我们先学习几个东西。
字节码
访问者模式
一个称为元素(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