asm项目结构
1. 项目结构
1.1. asm的仓库
1.2 核心项目
1.2.1 org.objectweb.asm 和 org.objectweb.asm.signature
包定义了基于事件的API,并提供了类分析器和写入器组件。它们包含在 asm.jar 中。
1.2.2 org.objectweb.asm.util
包,位于asm-util.jar中,提供各种基于核心 API 的工具,可以在开发和调试 ASM 应用程序时使用。
1.2.3 org.objectweb.asm.commons
包提供了几个很有用的预定义类转换器,它们大多是基于核心 API 的。这个包包含在 asm-commons.jar中。
1.2.4 org.objectweb.asm.tree
包,位于asm-tree.jar 存档文件中,定义了基于对 象的 API,并提供了一些工具,用于在基于事件和基于对象的表示方法之间进行转换。
1.2.5 org.objectweb.asm.tree.analysis
包提供了一个类分析框架和几个预定义的 类 分析器,它们以树 API 为基础。这个包包含在 asm-analysis.jar 文件中。
1.3 其他项目
可以看出来1.2
描述的几个包和1.1
仓库是基本对应的。
但是除了asm-all
和asm-parent
,与asm-debug
等。
1.3.1 asm-all和asm-parent
asm-all包含了asm-parent,asm-parent 包含了所有的依赖。
一般来讲,使用者希望导入一个总的jar,其中包含子项目的jar。但是asm-all目前只停留在version 5.2
,而最新子项目版本已经是8.1
,不推荐使用。
- asm-all 的pom
<project>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-parent</artifactId>
<version>5.2</version>
</parent>
<name>ASM All</name>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-all</artifactId>
<packaging>jar</packaging>
</project>
asm-parent
的pom
parent包含了子项目的依赖
<dependencyManagement>
<dependencies>
<dependency>
<artifactId>asm</artifactId>
<groupId>${project.groupId}</groupId>
<version>${project.version}</version>
</dependency>
<dependency>
<artifactId>asm-tree</artifactId>
<groupId>${project.groupId}</groupId>
<version>${project.version}</version>
</dependency>
<dependency>
<artifactId>asm-analysis</artifactId>
<groupId>${project.groupId}</groupId>
<version>${project.version}</version>
</dependency>
<dependency>
<artifactId>asm-commons</artifactId>
<groupId>${project.groupId}</groupId>
<version>${project.version}</version>
</dependency>
<dependency>
<artifactId>asm-util</artifactId>
<groupId>${project.groupId}</groupId>
<version>${project.version}</version>
</dependency>
<dependency>
<artifactId>asm-xml</artifactId>
<groupId>${project.groupId}</groupId>
<version>${project.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
1.4 maven依赖 asm的配置
并不推荐使用1.3
导入全部,因为版本太老了
这里开发者需要这么引入。asm-commons
和asm-util
都包含了,基本的 core api ,tree api, 和analysis
<properties>
<asm.version>8.0.1</asm.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<artifactId>asm</artifactId>
<groupId>org.ow2.asm</groupId>
<version>${asm.version}</version>
</dependency>
<dependency>
<artifactId>asm-tree</artifactId>
<groupId>org.ow2.asm</groupId>
<version>${asm.version}</version>
</dependency>
<dependency>
<artifactId>asm-analysis</artifactId>
<groupId>org.ow2.asm</groupId>
<version>${asm.version}</version>
</dependency>
<dependency>
<artifactId>asm-commons</artifactId>
<groupId>org.ow2.asm</groupId>
<version>${asm.version}</version>
</dependency>
<dependency>
<artifactId>asm-util</artifactId>
<groupId>org.ow2.asm</groupId>
<version>${asm.version}</version>
</dependency>
<dependency>
<artifactId>asm-xml</artifactId>
<groupId>org.ow2.asm</groupId>
<version>${asm.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<artifactId>asm-commons</artifactId>
<groupId>org.ow2.asm</groupId>
<version>${asm.version}</version>
</dependency>
<dependency>
<artifactId>asm-util</artifactId>
<groupId>org.ow2.asm</groupId>
<version>${asm.version}</version>
</dependency>
</dependencies>
核心的包都具备了,是版本8.0.1
2. IDEA的插件
装两个插件。
ASM Bytecode outline
与hexview
2.1 ASM Bytecode outline 插件
- 源码&字节码
- 等价的ASM API
2.1 hexview插件
仅仅是以16进制显示.class文件
这个是真正的字节码,而asm插件中的字节码,是以人类可读的方式处理过。类似javap
3. asm的使用示例
demo源码
这一步门槛比较高,需要对java 字节码的知识。和ASM 有全面的了解。
- java 字节码知识推荐
《深入理解jvm 字节码》
和jvm 规范
比对着看 - ASM 推荐阅读官方指导手册,写的很容易懂。博客里提供了中英两版,比对者看。
《深入理解jvm 字节码》
中的例子,使用基于事件的core api
和基于文档模型的tree API
。如果具备字节码结构的知识、字节码指令知识、栈帧的模型的知识,去理解下面的例子非常容易,因为对类的加工,本质是增删改查字节码内容而已。
3.1 源码 & 字节码
源码
package com;
public class Application {
public int a = 0;
public int b = 1;
public void test01() {
}
public void test02() {
}
}
字节码
javap -verbose Application.class
编译后的类不包含package和import信息。
Classfile /Users/sunqiyuan/Desktop/Work/mycode/Javadetail/BYTECODE/bytecode/target/classes/com/Application.class
Last modified 2020-6-30; size 464 bytes
MD5 checksum 66668ae198d146acc35018549c757af7
Compiled from "Application.java"
public class com.Application
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #5.#20 // java/lang/Object."<init>":()V
#2 = Fieldref #4.#21 // com/Application.a:I
#3 = Fieldref #4.#22 // com/Application.b:I
#4 = Class #23 // com/Application
#5 = Class #24 // java/lang/Object
#6 = Utf8 a
#7 = Utf8 I
#8 = Utf8 b
#9 = Utf8 <init>
#10 = Utf8 ()V
#11 = Utf8 Code
#12 = Utf8 LineNumberTable
#13 = Utf8 LocalVariableTable
#14 = Utf8 this
#15 = Utf8 Lcom/Application;
#16 = Utf8 test01
#17 = Utf8 test02
#18 = Utf8 SourceFile
#19 = Utf8 Application.java
#20 = NameAndType #9:#10 // "<init>":()V
#21 = NameAndType #6:#7 // a:I
#22 = NameAndType #8:#7 // b:I
#23 = Utf8 com/Application
#24 = Utf8 java/lang/Object
{
public int a;
descriptor: I
flags: ACC_PUBLIC
public int b;
descriptor: I
flags: ACC_PUBLIC
public com.Application();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: iconst_0
6: putfield #2 // Field a:I
9: aload_0
10: iconst_1
11: putfield #3 // Field b:I
14: return
LineNumberTable:
line 3: 0
line 4: 4
line 5: 9
LocalVariableTable:
Start Length Slot Name Signature
0 15 0 this Lcom/Application;
public void test01();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=0, locals=1, args_size=1
0: return
LineNumberTable:
line 9: 0
LocalVariableTable:
Start Length Slot Name Signature
0 1 0 this Lcom/Application;
public void test02();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=0, locals=1, args_size=1
0: return
LineNumberTable:
line 13: 0
LocalVariableTable:
Start Length Slot Name Signature
0 1 0 this Lcom/Application;
}
SourceFile: "Application.java"
3.2 访问类的方法和字段
core API
接受编译过的Application.class
的byte[]。ClassVisitor中覆盖FieldVisitor
和MethodVisitor
方法,进行了方法和field的打印。
本类中ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG
跳过对debug信息和方法Code属性的访问。
package com;
import org.objectweb.asm.*;
import static jdk.internal.org.objectweb.asm.Opcodes.ASM5;
/**
* 访问类的方法和区域
*/
public class B_visitContent extends ClassLoader {
public void visitByCoreAPI() throws Exception {
ClassReader cr = Tool.getClassReader();
// 使用 new ClassWriter(0) 时,不会自动计算任何东西。必须自行计算帧、局部变量与操作数栈的大小
ClassWriter cw = new ClassWriter(0);
ClassVisitor cv = new ClassVisitor(ASM5, cw) {
@Override
public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
System.out.println("field in visitor: " + name);
return super.visitField(access, name, descriptor, signature, value);
}
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
System.out.println("method in visitor: " + name);
return super.visitMethod(access, name, descriptor, signature, exceptions);
}
};
/*
ClassReader.SKIP_DEBUG 跳过类文件中的调试信息,比如行号信息(LineNumberTable)
ClassReader.SKIP_CODE 跳过方法体中的Code属性,比如(方法字节码、异常表等信息)
ClassReader.SKIP_DEBUG 展开StackMapTable属性
ClassReader.SKIP_DEBUG 跳过StackMapTable属性
*/
cr.accept(cv, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG);
}
public static void main(String[] args) throws Exception{
new B_visitContent().visitByCoreAPI();
}
}
打印结果
field in visitor: a
field in visitor: b
method in visitor: <init>
method in visitor: test01
method in visitor: test02
tree API
等同于上面的例子
package com;
import org.objectweb.asm.*;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.MethodNode;
import java.util.List;
import static jdk.internal.org.objectweb.asm.Opcodes.ASM5;
/**
* 访问类的方法和区域
*/
public class B_visitContent extends ClassLoader {
public void visitByTreeAPI() throws Exception {
ClassReader cr = Tool.getClassReader();
// 使用 new ClassWriter(0) 时,不会自动计算任何东西。必须自行计算帧、局部变量与操作数栈的大小
ClassNode cn = new ClassNode();
/*
ClassReader.SKIP_DEBUG 跳过类文件中的调试信息,比如行号信息(LineNumberTable)
ClassReader.SKIP_CODE 跳过方法体中的Code属性,比如(方法字节码、异常表等信息)
ClassReader.SKIP_DEBUG 展开StackMapTable属性
ClassReader.SKIP_DEBUG 跳过StackMapTable属性
*/
cr.accept(cn, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG);
List<FieldNode> fields = cn.fields;
for (int i = 0; i <fields.size() ; i++) {
FieldNode fn = fields.get(i);
System.out.println("field in visitor: " + fn.name);
}
List<MethodNode> methods = cn.methods;
for (int i =