ASM库

ASM是一个高性能的Java字节码分析和操作框架,用于动态生成和分析类。它提供了基于事件的核心API和基于对象的Tree API。通过继承ClassVisitor和实现相关方法,可以解析已有的类并进行输出。此外,ASM也可用于生成新的类和对类进行变形,例如在处理事件时进行过滤。示例展示了如何使用ASM打印类信息和生成类的字节码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 简介

官网:https://asm.ow2.io/
javadoc :https://asm.ow2.io/javadoc/org/objectweb/asm/package-summary.html

(the ASM name does not mean anything: it is just a reference to the __asm__ keyword in C, which allows some functions to be implemented in assembly language.)

ASM是一个多功能的java 字节码分析操控框架,可用于分析现有的类、或是动态的生成二进制的类。ASM提供了一些通用的字节码转化器和分析算法,复杂的自定义的转化器和分析工具可在此基础上进行二次开发。

另外,Java也有一些其他的字节码框架,但是ASM的主要特点是高性能,因为ASM一开始的设计目标就是代码短小、且尽可能的运行快。

asm作为一个java字节码分析操作框架,要了解asm,首先了解一下java字节码结构。

2. java字节码结构

相对于其他的语言的可执行文件,java字节码结构比较简单。一个字节码包含如下内容:

  • 类的修饰符(public,private)、类名、父类、接口和类的注解;
  • field 节,每个field都有一个field 节,描述该成员的修饰符、名字、类型、和该成员上的标注;
  • 方法节,每个方法(包括构造方法)都有一个method节,描述该方法的修饰符、名字、返回类型、参数列表、方法代码、和该成员上的标注;

字节码总体结构如下图所示:
在这里插入图片描述

java源码与字节码的区别:

  • 一个字节码只和一个class对应。但是一个java 类源文件中可以包含多个类(但是只有一个public的类);
  • 字节码中没有代码注释;
  • 字节码中没有package,import语句了。所有的类型都是全限定类名。
  • 另外,字节码中存在常量池节(constant pool section) 。该节是一个数组,包含该字节码中出现的所有的数自、字符串、类型常量。这些常量只定义一次,就是在常量池中,其他节中通过数组下标(index)进行引用。

另外,类的内部名字(internal name)使用全限定名描述,但是和源码中全限定名描述不同的是内部名字用斜杠代替点号,例如对于String,其内部名字是:java/lang/String

字节码中的类型描述符:
在这里插入图片描述

字节码中的方法描述符:

在这里插入图片描述

3.asm 对字节码的描述、建模与处理

ASM 提供了两种API来完成java字节码的处理工作

  • 基于事件(event)的核心API。这种方式是将一个类建模表示为事件序列。每个事件(event)表示类的一个成员,例如类的头部、成员、方法、或是方法中的一条指令,都是一个事件。基于事件的API定义了可能的事件、及事件的顺序约束,另外,还提供了类parser来解析字节码生成事件序列、类wirter来根据一系列的事件生成字节码。这种方式和XML解析中的SAX模型类似。

  • 基于对象(object)的tree API。这种方式是将一个类建模表示象树;每个对象表示一个类的一部分,例如成员、方法、指令等,都是对象树上的一个对象。基于对象的tree API是基于事件的API构建的。这种方式和XML解析中的DOM模型类似。

4.asm 的基于事件的核心API

基于事件的核心API主要以classVistor,ClassReader,ClassWriter来处理字节码。他们的作用如下:

  • ClassReader: 解析字节妈,然后调用相应的ClassVisitor对象的 visitXxx m方法(在调用ClassReaderaccept方法时,ClassReader会绑定一个ClassVistor对象)

  • ClassWriter :ClassWriterClassVisitor抽象类的子类,用于直接构建二进制形式的编译后的类;

  • classVistor :用于处理事件,Vistor之间形成一个责任链。一个处理完后会将控制传递到下一个vistor;

5. ASM 中的 ClassVisitor 抽象类

public abstract class ClassVisitor {
public ClassVisitor(int 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**();
}

ClassVisitor中的每个方法和字节码的结构对应。如果字节码中的section内容简单,则对应的visist方法会返回void。如果字节码中节的内容比较复杂、且内容长度可变,那么方法返回一个辅助visitor 类(例如,AnnotationVisitor,
FieldVisitor 或是 MethodVisitor),来继续访问。对于AnnotationVisitor,
FieldVisitorMethodVisitor的处理原则同ClassVisitor,对于子的复杂的节处理,又会返回一个辅助Vistor。例如,对于FieldVistor,其方法如下:

public abstract class FieldVisitor {
public FieldVisitor(int api);
public FieldVisitor(int api, FieldVisitor fv);

public AnnotationVisitor visitAnnotation(String desc, boolean visible);

public void visitAttribute(Attribute attr);

public void visitEnd();
}

在解析一个类时,调用的visitXX方法的顺序原则如下:

visit   visitSource?    visitOuterClass?     (   visitAnnotation |    visitAttribute )*
( visitInnerClass   | visitField   | visitMethod   )*
visitEnd

6.使用asm解析已有的类的示例

这是一个简单示例,对于已有的类,打印出来。类似与javap 工具。

6.1 继承ClassVistor类,打印出所有访问(visit)到的数据

public class ClassPrinter extends ClassVisitor {
	public ClassPrinter() {
		super(ASM4);
	}

	public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {

		// 输出访问到的类的header
		System.out.println(name + " extends " + superName + " {");
	}

	public void visitSource(String source, String debug) {
	}

	public void visitOuterClass(String owner, String name, String desc) {
	}

	public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
		return null;
	}

	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) {

		// 输出访问到的成员
		System.out.println(" " + desc + " " + name);
		return null;
	}

	public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {

		// 输出访问到方法
		System.out.println(" " + name + desc);
		return null;
	}

	public void visitEnd() {

		// 类解析完毕
		System.out.println("}");
	}
}

6.2 将ClassPrinter(ClassVistor) 与ClassReader绑定,使得ClassReader读取到的事件能被ClassPrinter消费

ClassPrinter cp = new ClassPrinter();
ClassReader cr = new ClassReader("java.lang.Runnable");
cr.accept(cp, 0); // 在这行ClassReader会去解析 Runable类,然后根据解析到内容,生成相关的事件,并调用ClassPrinter去消费处理

程序输出如下:

java/lang/Runnable extends java/lang/Object {
run()V
}

7.使用asm生成类

以如下接口为例进行说明:

package pkg;

public interface Comparable extends Mesurable {
	int LESS = -1;
	int EQUAL = 0;
	int GREATER = 1;

	int compareTo(Object o);
}

上述接口可通过调用ClasVistor 6次生成,调用形式如下:

ClassWriter cw = new ClassWriter(0);
//生成接口的header,包括名字,父接口,描述符
cw.visit(V1_5, ACC_PUBLIC + ACC_ABSTRACT + ACC_INTERFACE,
"pkg/Comparable", null, "java/lang/Object",
new String[] { "pkg/Mesurable" });

//生成一个属性成员
cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "LESS", "I",
null, new Integer(-1)).visitEnd();

//生成一个属性成员
cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "EQUAL", "I",
null, new Integer(0)).visitEnd();

//生成一个属性成员
cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "GREATER", "I",
null, new Integer(1)).visitEnd();

//生成一个方法
cw.visitMethod(ACC_PUBLIC + ACC_ABSTRACT, "compareTo",
"(Ljava/lang/Object;)I", null, null).visitEnd();

cw.visitEnd();

//结果保存在ClasWriter中,获取流式的结果
byte[] b = cw.toByteArray();

上述生成的类数据可以保存到文件中,也可以通过classloader进行加载到JVM中使用,例如:

class MyClassLoader extends ClassLoader {
	public Class defineClass(String name, byte[] b) {
		return defineClass(name, b, 0, b.length);
	}
}

然后使用自定义的类加载器进行加载:

Class c = myClassLoader.defineClass("pkg.Comparable", b);

8.使用ASM对类进行变形

对类进行变形的基本流程:使用ClassReader进行读取,然后调用ClassVistor处理,最后通过ClassWriter输出。

示例代码如下:

//类字节码以字节数组的方式读取,也可以以其他方式进行
byte[] b1 = ...;

ClassWriter cw = new ClassWriter(0);

// cv 会将所有的事件传递到cw上(classVistor之间形成链)
ClassVisitor cv = new ClassVisitor(ASM4, cw) { };

//指定cr要处理的字节码
ClassReader cr = new ClassReader(b1);

//cr开始解析,调用cv处理事件,cv会将事件传递到cw上
cr.accept(cv, 0);

//获取转化后的结果
byte[] b2 = cw.toByteArray(); // b2 represents the same class as b1

上述代码的处理过程如下:
在这里插入图片描述

8.1 事件过滤

如果某个Vistor只重写了父类ClassVistor的某些方法,则该vistor不会响应其他方法。例如,ChangerVersionAdapter继承了ClassVistor,只重写了visit方法,则它的上游(如ClassReader)在解析到类header section时,调用它的visit方法,在解析到其他事件时,就不会调用ChangerVersionAdapter 了。

示例:

public class ChangeVersionAdapter extends ClassVisitor {
	public ChangeVersionAdapter(ClassVisitor cv) {
		super(ASM4, cv);
	}

	@Override
	public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
		cv.visit(V1_5, access, name, signature, superName, interfaces);
	}
}

在这里插入图片描述

<think>好的,我现在需要帮助用户找到ASM的下载地址和使用方法。用户的问题是关于如何下载和使用ASM,还提到了官方文档和示例教程。首先,我应该确认ASM是什么。ASM是一个Java字节码操作和分析框架,广泛用于动态生成类或者增强既有类的功能。常见的用途包括AOP(面向切面编程)、代码插桩等。 接下来,用户需要下载地址。我需要回忆一下ASM的官方网站。通常,这类开源项目的官方网站可能是asm.ow2.io,对吧?然后,官方网站上应该有下载链接,可能是在下载页面或者文档部分。需要确保提供的链接是正确的,并且是最新版本。同时,Maven仓也是常用的依赖管理工具,用户可能希望通过Maven或Gradle来添加依赖,这样更方便。 然后是使用方法。用户可能需要基础的示例,比如如何读取和修改字节码。ASM提供了Core API和Tree API两种方式,Core API基于访问者模式,可能更适合性能要求高的场景,而Tree API则更直观。我应该给出一个简单的例子,比如修改一个类的方法,插入一些日志输出。同时,需要提到官方文档的重要性,指导用户查阅详细内容。 用户还可能希望看到示例教程的链接,但如果没有具体的站内教程,可能需要建议他们查看官方文档中的示例部分,或者推荐一些知名的第三方教程,比如Baeldung或者博客文章。但需要注意,如果引用外部链接,可能需要用户自行搜索,或者确保链接的可靠性。 另外,要检查是否有常见的错误或注意事项,比如版本兼容性,Java版本的要求,以及使用ASM时可能遇到的类加载问题。这些信息对用户来说可能很重要,避免他们在使用过程中遇到困难。 最后,按照用户的要求,生成相关问题。这些问题应该围绕ASM的扩展应用、核心功能、与其他工具的比较,以及遇到的常见问题。比如,如何用ASM实现AOP,或者ASM和Javassist的区别,这些都是常见的问题,可以帮助用户进一步了解ASM的应用场景和技术选型。 总结一下,回答的结构应该是先介绍ASM,然后下载方式,接着使用方法,包括代码示例和文档指引,最后是相关问题。需要确保所有步骤清晰,代码正确,并且提供可靠的资源链接。同时,注意用户提到的引用标识,但根据用户提供的参考内容,似乎没有具体的引用来源,所以可能不需要添加额外的引用标识,除非有明确的引用来源需要标注。</think>### 如何下载并使用ASM ASM是一个用于Java字节码操作和分析的框架,常用于动态生成类、修改类结构或优化代码。以下是具体步骤: #### 1. 下载ASM **官方地址**: - 官网:[ASM Official Site](https://asm.ow2.io/) - 下载页面:[ASM Releases on Maven Central](https://repo1.maven.org/maven2/org/ow2/asm/) 选择最新版本(例如9.6),下载`asm-9.6.jar`、`asm-util-9.6.jar`等所需模块[^1]。 **通过构建工具添加依赖**: - **Maven**: ```xml <dependency> <groupId>org.ow2.asm</groupId> <artifactId>asm</artifactId> <version>9.6</version> </dependency> ``` - **Gradle**: ```gradle implementation 'org.ow2.asm:asm:9.6' ``` #### 2. 基础使用方法 **示例:修改类的字节码** ```java import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassWriter; public class ASMExample { public static byte[] modifyClass(byte[] originalClass) { ClassReader reader = new ClassReader(originalClass); ClassWriter writer = new ClassWriter(reader, 0); // 添加自定义ClassVisitor以修改字节码 reader.accept(new MyClassVisitor(writer), 0); return writer.toByteArray(); } } ``` **官方文档与教程**: - [ASM官方指南](https://asm.ow2.io/asm4-guide.pdf) - 示例教程:[ASM Bytecode Outline插件](https://plugins.jetbrains.com/plugin/5918-asm-bytecode-outline)(用于IntelliJ IDEA,可实时查看字节码) #### 3. 注意事项 - **版本兼容性**:ASM版本需与Java版本匹配(例如ASM 9.x支持Java 21)。 - **性能优化**:优先使用`ClassReader.SKIP_DEBUG`跳过调试信息以提升解析速度。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值