在ASM3.3.1中,提供了7个jar包,分别是
asm-3.3.1.jar
asm-commons-3.3.1.jar
asm-tree-3.3.1.jar
asm-analysis-3.3.1.jar
asm-util-3.3.1.jar
asm-xml-3.3.1.jar
参看ASM的javadoc(http://asm.ow2.org/asm33/javadoc/user/index.html),可以看到一共有7个package,package和jar的对应关系如下
asm-3.3.1.jar 包含了org.objectweb.asm和org.objectweb.asm.signature两个packages
asm-commons-3.3.1.jar包含了org.objectweb.asm.commons这个package
asm-tree-3.3.1.jar 包含了org.objectweb.asm.tree这个package
asm-analysis-3.3.1.jar包含了org.objectweb.asm.tree.analysis这个package
asm-util-3.3.1.jar包含了org.objectweb.asm.util这个package
asm-xml-3.3.1.jar包含了org.objectweb.asm.xml这个package
其中asm-3.3.1.jar,是包含了核心的功能,而其他的jar,都是基于这个核心的扩展。
我们这里来看一下,如何读写字节码
ClassReader和ClassVisitor
如大家所了解的,ASM是一个操作字节码(bytecode)的框架,非常的小巧和快速,这个asm-3.3.1.jar,只有43k的大小。
asm提供了字节码的读写的功能。而asm的核心,采用的是visitor的模式,提供了ClassReader和ClassWriter这两个非常重要的类以及ClassVisitor这个核心的接口。
ClassReader的职责是读取字节码。可以用InputStream、byte数组、类名(需要ClassLoader.getSystemResourceAsStream能够加载到的class文件)作为构造函数的参数构造ClassReader对象,来读取字节码。而ClassReader的工作,就是根据字节码的规范,从输入中读取bytecode。而通过ClassReader对象,获取bytecode信息有两种方式,一种就是采用visitor的模式,传入一个ClassVisitor对象给ClassReader的accept方法。另外一种,是使用Low Level的方式,使用ClassReader提供了readXXX以及getXXX的方法来获取信息。
对于一般使用,用ClassReader的accept方法,使用visitor模式就可以了。
那么接着我们来看一下ClassVisitor。
ClassVisitor这个接口,定义了一系列的visit方法,而这些visit方法,我们通过实现ClassVisitor接口中的visit方法(其实就是一堆的回调函数),就能够得到相应的信息。
在asm中,ClassAdapter这个类,用asm的javadoc上的话说,就是一个代理到其他ClassVisitor的一个空的ClassVisitor(An empty ClassVisitor that delegates to another ClassVisitor.)。具体来说,构造ClassAdapter对象的时候,需要传递一个ClassVisitor的对象给ClassAdapter的构造函数,而ClassAdapter对ClassVisitor的实现,就是直接调用这个传给ClassAdapter的ClassVisitor对象的对应visit方法。
后面我们会看到一个使用ClassAdapter的例子。
说到这里,我们主要介绍了ClassReader这个类,ClassVisitor这个接口以及ClassAdapter。那么我们先看一个简单的例子,是使用ClassReader和ClassVisitor的一个示例。
package org.vanadies.bytecode.example.asm3;
import java.io.IOException;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
public class ClassReaderExample {
private static class MyClassVisitor implements ClassVisitor {
@Override
public void visit(int version, int access, String name,
String signature, String superName, String[] interfaces) {
System.out.println("class name:" + name);
System.out.println("super class name:" + superName);
System.out.println("class version:" + version);
System.out.println("class access:" + access);
System.out.println("class signature:" + signature);
if(interfaces != null && interfaces.length > 0){
for(String str : interfaces){
System.out.println("implemented interface name:" + str);
}
}
}
@Override
public void visitSource(String source, String debug) {
}
@Override
public void visitOuterClass(String owner, String name, String desc) {
}
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
return null;
}
@Override
public void visitAttribute(Attribute attr) {
}
@Override
public void visitInnerClass(String name, String outerName,
String innerName, int access) {
}
@Override
public FieldVisitor visitField(int access, String name, String desc,
String signature, Object value) {
return null;
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc,
String signature, String[] exceptions) {
return null;
}
@Override
public void visitEnd() {
}
}
public static void main(String args[]) throws IOException{
ClassReader classReader = new ClassReader("java.lang.String");
classReader.accept(new MyClassVisitor(), 0);
}
}
这个简单的例子,打印了String这个类的基本信息。
从上面的例子看,ClassVisitor这个接口的方法中,有些返回值是void,有些是返回了XXXXVisitor。
这里,返回XXXVisitor的,我们举个例子,比如visitMethod这个方法,返回的是MethodVisitor。这里,在visitMethod的方法中,给出了Method的基本信息,如果需要去获取Method内部的详细的信息(包括代码,注解等),那么需要返回一个MethodVisitor对象出去,如果返回null,那么在ClassReader中,就不会去展开对这个Method的详细信息进行遍历了。
除了MethodVisitor,还有,AnnotationVisitor,FieldVisitor。
另外,在org.objectweb.asm.signature这个包中,有一个SignatureVisitor以及SignatureReader和SignatureWriter,这个是处理Signature的。
而除了ClassAdapter外还有MethodAdapter,作用类似与ClassAdapter,只是MethodAdapter是针对MethodVisitor的。
到这里,我们已经了解了如何读取bytecode的信息了,也了解了如何使用ClassReader和ClassVisitor了。 接下来,我们要看一下,如果使用ClassWriter这个类了。 ClassWriter,顾名思义,是用来写字节码的。ClassWriter实现了ClassVisitor这个接口。这里可能大家会觉得奇怪,ClassReader什么接口都没有实现,为啥ClassWriter要实现ClassVisitor的接口呢?ClassWriter只要提供了方法,让我写字节码就行了哇。
大家还记得,通过ClassReader获取类的字节码,有两种方式,一种是使用Visitor模式,这种方法很简单,前面也有demo,另外是使用low level的get和read来进行,这个很复杂。而如果ClassWriter提供的是low level的put和write这类的方法,会提高门槛,很不好用。而ClassWriter实现了ClassVisitor接口,那么就能够很好的跟ClassReader的Visitor模式结合起来。并且,我们使用ASM操作字节码,在写方面更多的是修改、添加和删除,而不是用ASM来完全去写新的class。
ClassWriter中,还有一个toByteArray的方法,这个是把ClassWriter对象生成的字节码,写到一个byte数组中。用于我们来获取字节码的最终结果。
那么,我们可以做一个很简单的例子。就是把ClassWriter对象作为ClassReader accept方法的参数,传给ClassReader,然后在accept方法结束后,我们用ClassWriter对象的toByteArray方法,就能够获得类的字节码了。这个例子,就不贴源码了,有兴趣的同学,可以自己搞一下。
那么,我们把ClassWriter用在哪里呢?上文提到了,删除、修改、增加。那么一般来说,删除我们是不太用的。主要是修改和增加。
比方说我们如果自己要做AOP,用到的就是修改和增加。假如我有一个类,其中有一个execute方法,我如果自己做AOP,我可以把这个execute方法改名,比如叫做execute$1,然后我增加一个execute方法,新的execute方法中,我可以调用原来的execute方法(现在是execute$1)了,并且在调用前后做一个处理。
我们来看一下具体代码
package org.vanadies.bytecode.example.asm3;
import org.objectweb.asm.ClassAdapter;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
public class ClassWriterAopExample {
public static class Foo {
public void execute(){
System.out.println("Hello World");
}
}
public static void main(String[] args) throws Exception{
Foo foo = new Foo();
foo.execute();
ClassReader cr = new ClassReader(Foo.class.getName());
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
cr.accept(new ClassAdapter(cw){
@Override
public void visit(
final int version,
final int access,
final String name,
final String signature,
final String superName,
final String[] interfaces)
{
cv.visit(version, access, name + "$1", signature, superName, interfaces);
}
@Override
public MethodVisitor visitMethod(
final int access,
final String name,
final String desc,
final String signature,
final String[] exceptions)
{
if("execute".equals(name)){
//这里只是简单的比较了方法名字,其实还需要比较方法参数,参数信息在desc中
return cv.visitMethod(access, name + "$1", desc, signature, exceptions);
}
return cv.visitMethod(access, name, desc, signature, exceptions);
}
}, 0);
//到这里,如果调用cr.toByteArray,生成的字节码中,已经没有execute方法了,而是execute$1
//我们接着需要增加一个execute方法
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "execute",
"()V", null,
null);
//开始增加代码
mv.visitCode();
//接下来,我们需要把新的execute方法的内容,增加到这个方法中
mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
mv.visitLdcInsn("Before execute");
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V");
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "org/vanadies/bytecode/example/asm3/ClassWriterAopExample$Foo$1", "execute$1", "()V");
mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
mv.visitLdcInsn("End execute");
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V");
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(0, 0); //这个地方,最大的操作数栈和最大的本地变量的空间,是自动计算的,是因为构造ClassWriter的时候使用了ClassWriter.COMPUTE_MAXS
mv.visitEnd();
//到这里,就完成了execute方法的添加。
//可以把字节码写入到文件,用javap -c,来看下具体的内容
}
}
这个代码,要比之前的ClassReader的例子要复杂一些。主要复杂的地方在重写execute方法那里,其实就是类似在用汇编在写代码。具体怎么来写或者生产这个代码,有很多办法。
我们这里只是先展示一下如果能够去修改字节码。这个例子展示了,修改原有方法名字,以及增加方法的做法,其他的增加字段等,都可以通过ClassWriter来处理。另外,需要注意的是,在3.0的ASM中,构造函数的名字,一定要使
用<init>,而不能使用“”,之前看到在2.x中,貌似有这样的用法,但是没有去验证,在网上别人的例子中,也看到过methodName使用“”来表示构造函数的,这个在3.x中是不对的。
具体在运行时能够改变类的行为,我们可以通过Instrumentation的方式或者采用自己实现的ClassLoader的方式来处理。上面例子中,只是展示了如何修改字节码,这个和实际在运行时达到AOP的效果,还是有距离的。
分享到:
相关推荐
ASM 是一个 Java 字节码操控框架。它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。Java class 被存储在严格格式定义的 .class ...
ASM是一个强大的Java字节码操控和分析框架,它可以直接生成和修改Java类和注解的字节码。这个框架主要用于动态代理、代码分析以及优化等场景。在Java开发中,当我们需要在运行时生成或者修改类的行为时,ASM提供了一...
【标题】"cglib-2.2.jar asm-tree.jar asm-commons.jar asm.jar" 提供的是一组用于Java编程的库,它们主要用于实现动态代理和字节码操作。 【描述】"cglib动态代理模式jar包 cglib-2.2.jar asm-tree.jar asm-...
asm-util-1.3.4.jar, asm-util-1.3.5.jar, asm-util-1.4.1.jar, asm-util-1.4.3.jar, asm-util-1.5.1.jar, asm-util-1.5.2.jar, asm-util-1.5.3.jar, asm-util-2.0.jar, asm-util-2.1.jar, asm-util-2.2.1-sources....
这些文件是Java编程语言中用于动态代码生成和字节码操作的重要库,主要涉及ASM和CGLIB两个框架。ASM是一个轻量级的Java字节码操控和分析框架,而CGLIB是一个强大的代码生成库,它在许多情况下作为Spring框架的依赖...
赠送jar包:asm-9.1.jar; 赠送原API文档:asm-...使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,即可纵览文档内容。 人性化翻译,文档中的代码和结构保持不变,注释和说明精准翻译,请放心使用。
赠送jar包:asm-all-5.0.2.jar;...使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,即可纵览文档内容。 人性化翻译,文档中的代码和结构保持不变,注释和说明精准翻译,请放心使用。
提到的“mastering-java-bytecode.pdf”可能是一本关于Java字节码和ASM的教程,它可能涵盖了字节码的结构、ASM库的使用方法、实际案例分析等内容,是深入理解这两个主题的好资源。 7. **应用实例**: - Spring ...
标题中的“深入字节码 -- 玩转 ASM-Bytecode1”暗示了本文将探讨如何使用ASM库来操作和理解Java字节码。ASM是一个Java字节码操控和分析框架,它允许用户动态生成和修改Java类和注解。在描述中提到,前一篇博文中介绍...
ASM是一个Java字节码操控和分析框架,常用于动态代理、代码生成以及Java字节码的深度分析。这个文件可能是为了帮助开发者理解和调试ASM库的内部工作原理,因为源代码提供了比编译后的字节码更直观的视图。 描述中...
赠送jar包:asm-4.2.jar; 赠送原API文档:asm-...使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,即可纵览文档内容。 人性化翻译,文档中的代码和结构保持不变,注释和说明精准翻译,请放心使用。
`asm-tree`是Java字节码处理框架ASM的一个组件,主要负责将字节码解析成抽象语法树(Abstract Syntax Tree, AST)。ASM是一个强大的库,它允许程序动态生成和分析Java类。在Java世界中,这通常用于创建元编程框架、...
ASM是一个Java字节码操控和分析框架,它能够用来动态生成类或者增强已有类的功能。ASM库通常被用于编译器、代码分析工具以及一些需要在运行时修改Java类的框架。 描述中提到的“asm, util, jar.zip包下载, 依赖包”...
ASM是一个流行的Java字节码操作和分析框架,用于动态生成和转换类以及接口。ASM-3.1.jar是ASM框架的版本3.1的实现,主要用于处理和操作Java字节码。这个版本在ASM的早期发展中引入了重要的改进和修复,以支持更广泛...
ASM Commons是ASM库的一个扩展,ASM是一个广泛使用的Java字节码操控和分析框架。ASM Commons提供了在ASM基础上构建更高级别的工具所需的便利类和辅助方法。 描述中提到了"asm,commons,2.2.1,sources,jar.zip包下载,...
开发工具 asm-5.1开发工具 asm-5.1开发工具 asm-5.1开发工具 asm-5.1开发工具 asm-5.1开发工具 asm-5.1开发工具 asm-5.1开发工具 asm-5.1开发工具 asm-5.1开发工具 asm-5.1开发工具 asm-5.1开发工具 asm-5.1开发工具...
ASM是一个Java字节码操控和分析框架,它可以直接用来动态生成类或者增强已有类的功能。这个“all”通常意味着这个包包含了ASM所有模块,包括核心库、类访问器、注解处理器等,使得开发者无需分别导入多个JAR文件就...
kmod-oracleasm-2.0.8-15.el6_9.x86_64 oracleasm-support-2.1.8-1.el6.x86_64 oracleasmlib-2.0.4-1.el6.x86_64 安装顺序: rpm -ivh kmod-oracleasm-2.0.8-15.el6_9.x86_64.rpm rpm -ivh oracleasm-support-2.1.8...
ASM是一个Java字节码操控和分析框架,它可以直接用来动态生成类或者增强已有类的功能。在Java开发领域,ASM库因其强大的字节码操作能力而备受青睐,尤其在AOP(面向切面编程)、代码生成以及动态代理等场景中有着...
资源包含asm-all-3.0.jar等jar包,可以解决程序中因asm版本引起的兼容性错误。其他相关的jar如下: asm-all-3.0.jar asm-2.2.3.jar asm.jar asm-attrs.jar asm-commons-2.2.3.jar asm-util-2.2.3.jar