ASM学习笔记1 - 初识ClassVistor ——以ClassReader的应用为例

1 ASM简介

什么是ASM?

Java操纵类字节码的工具。是一个jar包。

如何使用?

ASM提供两类API,能够分别将类表示为事件对象。我们先主要了解核心API,即能够将类以事件方式操纵的方式,即用基于事件的模型

在采用基于事件的模型时,类是用一系列事件来表示的,每个事件表示类的一个元素,比 如 它的一个标头、一个字段、一个方法声明、一条指令,等等。基于事件的 API 定义了一组 可能 事件,以及这些事件必须遵循的发生顺序,还提供了一个类分析器,为每个被分析元素生 成一个 事件,还提供一个类写入器,由这些事件的序列生成经过编译的类。

下文会介绍该API来使用。

2 类操作

这一部分将会描述asm对java类文件的操作。在这之前,我们需要对java类本身的结构有一定了解。

class知识点

  1. 在class内,数据类型以类型描述符表示。一些常用的类型标识符,如:int,表示为I;int数组,表示为[I。而对于非原生数据类型,如用户自定类型,String,Object类型,则使用L+内部名+;表示。内部名是类的完全限定名,以层级关系以/分割。例如,String 的内部名为 java/lang/String。因此,String的类型描述符为Ljava/lang/String;,String数组的描述符为[Ljava/lang/String;。
  2. 同样,方法也有方法描述符。具体方式如下:

返回值(变量1,变量2,...)→(变量1描述符+变量2描述符,...)返回值描述符返回值(变量1,变量2,...) \rightarrow (变量1描述符+变量2描述符,...)返回值描述符 返回值(变量1,变量2,...)→(变量1描述符+变量2描述符,...)返回值描述符

例如,一个接受两个int为输入,String为输出的方法,方法描述符为(II)Ljava/lang/string;。注意,若输入为空,则括号内不填。若输出为空,则用V表示。

接下来开始正经介绍相关API:

3 ClassVistor

ClassVistor用于访问class,本身是抽象类。**定义在读取Class字节码时会触发的事件。**只要将所需执行的操作写入对应方法下,调用ClassVistor的其他类就能在对应的条件下触发他们。

以下为一些API:

public ClassVisitor(int api, ClassVisitor cv);
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces);
public void visitSource(String source, String debug);
public void visitOuterClass(String owner, String name, String desc);
AnnotationVisitor visitAnnotation(String desc, boolean visible);
public void visitAttribute(Attribute attr);
public void visitInnerClass(String name, String outerName, String innerName, int access);
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value);
public MethodVisitor visitMethod(int access, String name,String desc,String signature, String[] exceptions);
void visitEnd();

注意,ClassVistor对方法的访问是有顺序的:

这意味着必须首先调用 visit,然后是对 visitSource 的最多一个调用,接下来是对 visitOuterClass 的最多一个调用 , 然后是可按任意顺序对 visitAnnotation 和 visitAttribute 的任意多个访问 , 接下来是可按任意顺序对 visitInnerClass 、 visitField 和 visitMethod 的任意多个调用,最后以一个 visitEnd 调用结束。

一般我们使用时,只关心对类内的方法和域的访问。因此,我们只需了解在调用visitMethod与visitField前后需要使用visit和visitEnd即可。

4 ClassReader

该类类如其名,用来“阅读”class文件,即分析一个已经存在的类每当有事件发生时,调用注册的ClassVisitor、AnnotationVisitor、FieldVisitor、MethodVisitor做相应的处理。

构造器:

使用InputStream或byte[]获取类的字节,或直接通过String的类名称来获取类。

例:

ClassReader cr = new ClassReader("java.util.ArrayList");
System.out.println(cr.getSuperName());

我们使用了String作为输入的构造器,ClassReader接下来会处理ArrayList这个类。在第二行中,其便打印出了它的超类。以下为输出:

java/util/AbstractList

ClassReader也可分析用户类。例如IDEA中,我们需要访问clazz包下的Hello.class:

ClassReader cr = new ClassReader("clazz.Hello");

方法:

ClassReader提供了一系列get方法获取类信息:

不过,它最重要的方法还是accept方法:

accept可以接受一个ClassVisitor,接收后便开始读取数据。当满足一定条件时,就会触发ClassVisitor下的方法。且看以下代码:

import jdk.internal.org.objectweb.asm.ClassReader;
import jdk.internal.org.objectweb.asm.ClassVisitor;
import jdk.internal.org.objectweb.asm.MethodVisitor;import java.io.IOException;import static jdk.internal.org.objectweb.asm.Opcodes.*;public class Test {public static void main(String[] var0) throws IOException {ClassReader cr = new ClassReader("java.util.ArrayList");cr.accept(new MyClassVisitor(ASM4),0);}static class MyClassVisitor extends ClassVisitor {public MyClassVisitor(int i) {super(i);}@Overridepublic MethodVisitor visitMethod(int i, String s, String s1, String s2, String[] strings) {System.out.println("访问方法 " + s);return super.visitMethod(i, s, s1, s2, strings);}}
}

我们继承了ClassVisitor类并重写了visitMethod方法。还记得我们之前所说的吗?ClassVistor定义了在读取Class字节码时会触发的事件。我们通过accept方法,建立了ClassVisitor与ClassReader之间的连接。因此,当ClassReader访问对象的方法时,它将触发ClassVisitor内的visitMethod方法,这时由于我们在visitMethod下添加了一条println语句,这样我们就能获取所有的方法名了。

上述代码执行结果如下:

访问方法 <init>
访问方法 <init>
访问方法 <init>
访问方法 trimToSize
访问方法 ensureCapacity
访问方法 calculateCapacity
访问方法 ensureCapacityInternal
访问方法 ensureExplicitCapacity
访问方法 grow
访问方法 hugeCapacity
访问方法 size
访问方法 isEmpty
访问方法 contains
访问方法 indexOf
访问方法 lastIndexOf
访问方法 clone
访问方法 toArray
访问方法 toArray
访问方法 elementData
访问方法 get
访问方法 set
访问方法 add
访问方法 add
访问方法 remove
访问方法 remove
访问方法 fastRemove
访问方法 clear
访问方法 addAll
访问方法 addAll
访问方法 removeRange
访问方法 rangeCheck
访问方法 rangeCheckForAdd
访问方法 outOfBoundsMsg
访问方法 removeAll
访问方法 retainAll
访问方法 batchRemove
访问方法 writeObject
访问方法 readObject
访问方法 listIterator
访问方法 listIterator
访问方法 iterator
访问方法 subList
访问方法 subListRangeCheck
访问方法 forEach
访问方法 spliterator
访问方法 removeIf
访问方法 replaceAll
访问方法 sort
访问方法 access$000
访问方法 <clinit>

这样我们就获取了ArrayList下的所有方法。

更进一步:MethodVisitor和ClassVisitor的本质是一样的。既然我们通过了visitMethod能够返回MethodVisitor对象,我们能不能更进一步,找出ArrayList下的方法内又使用了那些方法呢?提示:MethodVisitor下的visitMethodInsn方法也会在访问方法时触发。建议读者在上述的代码的基础上自己尝试。以下是参考答案:

import com.sun.xml.internal.ws.org.objectweb.asm.*;
import jdk.internal.org.objectweb.asm.ClassReader;
import jdk.internal.org.objectweb.asm.ClassVisitor;
import jdk.internal.org.objectweb.asm.MethodVisitor;import java.io.IOException;
import java.util.ArrayList;import static jdk.internal.org.objectweb.asm.Opcodes.*;public class Test {public static void main(String[] var0) throws IOException {ClassReader cr = new ClassReader("java.util.ArrayList");cr.accept(new MyClassVisitor(ASM4),0);}static class MyClassVisitor extends ClassVisitor {public MyClassVisitor(int i) {super(i);}@Overridepublic MethodVisitor visitMethod(int i, String s, String s1, String s2, String[] strings) {System.out.println("访问方法 " + s);MethodVisitor mv = super.visitMethod(i, s, s1, s2, strings);return new MyMethodVisitor(ASM4,mv);}}static class MyMethodVisitor extends MethodVisitor {public MyMethodVisitor(int i, MethodVisitor methodVisitor) {super(i, methodVisitor);}@Overridepublic void visitMethodInsn(int i, String s, String s1, String s2, boolean b) {System.out.println("\t-> " + s1);super.visitMethodInsn(i, s, s1, s2, b);}}
}

输出如下:

访问方法 <init>-> <init>-> <init>-> append-> append-> toString-> <init>
访问方法 <init>-> <init>
访问方法 <init>-> <init>-> toArray-> getClass-> copyOf
访问方法 trimToSize-> copyOf
访问方法 ensureCapacity-> ensureExplicitCapacity
访问方法 calculateCapacity-> max
访问方法 ensureCapacityInternal-> calculateCapacity-> ensureExplicitCapacity
访问方法 ensureExplicitCapacity-> grow
访问方法 grow-> hugeCapacity-> copyOf
访问方法 hugeCapacity-> <init>
访问方法 size
访问方法 isEmpty
访问方法 contains-> indexOf
访问方法 indexOf-> equals
访问方法 lastIndexOf-> equals
访问方法 clone-> clone-> copyOf-> <init>
访问方法 toArray-> copyOf
访问方法 toArray-> getClass-> copyOf-> arraycopy
访问方法 elementData
访问方法 get-> rangeCheck-> elementData
访问方法 set-> rangeCheck-> elementData
访问方法 add-> ensureCapacityInternal
访问方法 add-> rangeCheckForAdd-> ensureCapacityInternal-> arraycopy
访问方法 remove-> rangeCheck-> elementData-> arraycopy
访问方法 remove-> fastRemove-> equals-> fastRemove
访问方法 fastRemove-> arraycopy
访问方法 clear
访问方法 addAll-> toArray-> ensureCapacityInternal-> arraycopy
访问方法 addAll-> rangeCheckForAdd-> toArray-> ensureCapacityInternal-> arraycopy-> arraycopy
访问方法 removeRange-> arraycopy
访问方法 rangeCheck-> outOfBoundsMsg-> <init>
访问方法 rangeCheckForAdd-> outOfBoundsMsg-> <init>
访问方法 outOfBoundsMsg-> <init>-> append-> append-> append-> append-> toString
访问方法 removeAll-> requireNonNull-> batchRemove
访问方法 retainAll-> requireNonNull-> batchRemove
访问方法 batchRemove-> contains-> arraycopy-> arraycopy
访问方法 writeObject-> defaultWriteObject-> writeInt-> writeObject-> <init>
访问方法 readObject-> defaultReadObject-> readInt-> calculateCapacity-> getJavaOISAccess-> checkArray-> ensureCapacityInternal-> readObject
访问方法 listIterator-> <init>-> append-> append-> toString-> <init>-> <init>
访问方法 listIterator-> <init>
访问方法 iterator-> <init>
访问方法 subList-> subListRangeCheck-> <init>
访问方法 subListRangeCheck-> <init>-> append-> append-> toString-> <init>-> <init>-> append-> append-> toString-> <init>-> <init>-> append-> append-> append-> append-> append-> toString-> <init>
访问方法 forEach-> requireNonNull-> accept-> <init>
访问方法 spliterator-> <init>
访问方法 removeIf-> requireNonNull-> <init>-> test-> set-> <init>-> nextClearBit-> <init>
访问方法 replaceAll-> requireNonNull-> apply-> <init>
访问方法 sort-> sort-> <init>
访问方法 access$000
访问方法 <clinit>

ASM学习笔记1 - 初识ClassVistor ——以ClassReader的应用为例相关推荐

  1. ASM学习笔记2 - 类的创建和修改 —— ClassWriter的综合应用

    ASM学习笔记2 - 类的创建和修改 -- ClassWriter的综合应用 上回我们说到,通过使用ClassVisitor和ClassReader,我们能够分析已经存在的类.这一节中,我们将使用Cl ...

  2. Hadoop学习笔记—4.初识MapReduce

    一.神马是高大上的MapReduce MapReduce是Google的一项重要技术,它首先是一个 编程模型 ,用以进行大数据量的计算.对于大 数据量的计算,通常采用的处理手法就是并行计算.但对许多开 ...

  3. oracle11g中用asmlib配置磁盘组,ASM学习笔记_配置ASMLIB磁盘组

    ASM学习笔记_配置ASMLIB磁盘组 目录 1 ASMLIB Introduction 2 虚拟机添加一个共享磁盘(块设备) 3 下载,安装ASMLIB 4 配置,使用ASMLib 磁盘组 #### ...

  4. C语言学习笔记-P1 初识C语言(2)

    C语言学习笔记-P1 初识C语言(2) C语言学习笔记-P1 初识C语言(2) 一.常量 1.字面常量 2.const修饰的常变量 3.#define定义的标识符常量 3.枚举常量 二.字符串+转义字 ...

  5. C语言学习笔记-P1 初识C语言(1)

    C语言学习笔记-P1 初识C语言(1) P1 初识C语言(1) 一.什么是C语言 1.定义 2.发展 二.第一个C语言程序 Hello World 三.数据类型 四.变量,常量 未完待续!!! P1 ...

  6. xilinx srio ip学习笔记之初识srio

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 xilinx srio ip学习笔记之初识srio 前言 IP 设置 总结 前言 因为工作原因,需要对rapidio 的协议进行了解, ...

  7. 吴恩达《机器学习》学习笔记一——初识机器学习

    吴恩达<机器学习>学习笔记一 一. 什么是机器学习? 二.监督学习 三.无监督学习 初识机器学习 这是个人学习吴恩达<机器学习>课程的一些笔记,供自己和大家学习提升.第一篇内容 ...

  8. 【人工智能学习笔记】初识人工智能

    文章目录 前言 一.人工智能的定义 二.人工智能的应用 1.计算机视觉技术 2.自然语言处理 3.其他具体应用举例 三.AI算法工程师主要工作流程 四.人工智能的基本概念 机器学习 有监督学习 VS ...

  9. C语言学习笔记之初识

    初识C语言 一.计算机和编程语言 1.初学者在学习c的时候,不仅会为语言本身的复杂而困惑,而且对其编程工具的选择同样困扰. 因为c语言出现的时间很早,而且在这么多年中也没有一个主要的组织对其编程工具进 ...

最新文章

  1. 01.几张图轻松理解String.intern()
  2. 《2020年AI新基建发展白皮书》重磅发布,展示百度智能云多个落地案例
  3. java dotnet core_在.NET Core 2.0中使用Omron库
  4. 【opencv学习笔记八】创建TrackBar轨迹条
  5. windbg-获取结构体大小(常用的两种方式)
  6. 信息学奥赛一本通(1012:计算多项式的值)
  7. Standard C++ Episode 10
  8. Block Formatting Contexts(块级格式化上下文)
  9. VC++6.0 按F1无法打开 MSDN 的解决办法
  10. python零基础学习书-零基础学习python推荐几本书?
  11. fir.im Weekly - 聊聊 Google 开发者大会
  12. 【MAVEN】搜索错误“Index downloads are disabled,search results may be incomplete”
  13. 给Intel AX200装上个Killer 1650X驱动
  14. C语言改变运行界面的颜色以及清屏功能
  15. 企业微信发送信息异常的临时处理方案
  16. 数据结构 —— 队列
  17. Android 中Goolgle 相关服务的移植[转]
  18. UEFI开发探索42 – Protocol的使用1
  19. 金融系列-会计基础知识
  20. 如何在A4相纸上打印4张5寸相片

热门文章

  1. 计算机硬件组装的图片,如何组装电脑?_百度经验
  2. 阿里巴巴FastJson整理(20分钟阅读)
  3. html div 移除,js动态创建及移除div的方法
  4. 有人负责,才有质量:写给在集市中迷失的一代
  5. QT 界面_滑屏窗口管理器(功能)
  6. Vue中使用微信JSDK实现图片上传
  7. 输出全靠画html5在线玩4399,输出全靠画
  8. 用友U9 SOA引领企业IT架构全面升级
  9. Ubuntu 18.04 桌面卡死
  10. c语言int输入1输出60000,数字的秘密问题分析(C语言)