本篇是本系列的第二篇,主要介绍什么是ASM,以及如何使用ASM。 如果没有阅读之前的,还请阅读一下字节码也能做有趣的事,因为需要上一章的部分知识。
在上节我们知道,通过javac编译生成之后生成的是字节码,但是我们可能会有一些需求,比如需要AOP切面,事务的统一管理,有些重复的代码需要我们来回的敲,又或者我们需要生成自己的字节码来使用(fastjson就是这么做的)。但是字节码如果我们直接操作,成本太大,并且效率也不高。这个时候你就需要一款利器,将字节码转换成java语言,从而你就可以随心所欲的操纵字节码。这些工具如ASM,例如Javaassit,BCEL等等,都可以用来操作字节码。 而这里我要介绍的就是操作字节码的一把利器-ASM,ASM是一个java字节码操纵框架,它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。Java class 被存储在严格格式定义的 .class文件里,这些类文件拥有足够的元数据来解析类中的所有元素:类名称、方法、属性以及 Java 字节码(指令)。ASM从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。 ASM的优点如下:
在这个小标题我会简单的介绍,如何去使用ASM。在这里之前我希望你有idea编译器,如果你有的话,可以去插件库里面下载一个ASM Bytecode Outline。有了这个我们后面开发ASM将会感受到美滋滋,如鱼得水。
ASM 库?供了两个用于生成和转换已编译类的API,一个是核心API,以基于事件的形式来表示类,另一个是树API,以基于对象的形式来表示类。
没有asm jar包的同学需要引入下面的maven:
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>5.0.4</version>
</dependency>
这里大家是否已经下好了那个插件(ASM Bytecode Outline)呢?如果下载完毕,还记得我们上一节的那个例子吗?
public class ByteCodeDemo {
private static final String name = "xiaoming";
private int age;
public ByteCodeDemo(int age) {
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(){
this.age = age;
}
public static void main(String[] args) {
ByteCodeDemo byteCodeDeomo = new ByteCodeDemo(12);
System.out.println("name:" + name + "age:" + byteCodeDeomo.getAge());
}
}
在这个类中,右键点击下方
然后右方会生成我们的如果用ASM生成这个类,那么应该是哪些代码:
复制旁边的代码,你就能生成你的class的二进制文件。
如果你看不懂,没关系,我这里会慢慢的讲。
在ASM的core API编程中有几个关键类:
public static void main(String[] args) throws Exception {
FileInputStream fileInputStream = new FileInputStream("java/java8/ByteCodeDemo.class");
ClassReader classReader = new ClassReader(fileInputStream);
ClassWriter cw = new ClassWriter(classReader, ClassWriter.COMPUTE_MAXS);
//Java8选择ASM5,
ClassVisitor classVisitor = new ClassVisitor(Opcodes.ASM5, cw) {
@Override
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
System.out.println("field:" + name);
return super.visitField(access, name, desc, signature, value);
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
System.out.println("方法" + name);
return super.visitMethod(access, name, desc, signature, exceptions);
}
};
//忽略调试信息
classReader.accept(classVisitor, ClassReader.SKIP_DEBUG);
}
我们下面输出:
field:name
field:age
方法<init>
方法getAge
方法setAge
方法main
可以看到我们已经通过visitField和visitMethod,进行对每个field和每个Method的名字都进行了输出,其中方法包括了编译器帮我们创建的构造方法和我们自定义的三个方法。
由于后面的代码都会通过coreApi来做,这里树形API简单用例子说明一下:
public static void main(String[] args) throws Exception {
FileInputStream fileInputStream = new FileInputStream("/Users/lizhao/Documents/RPC/test/src/main/java/java8/ByteCodeDemo.class");
ClassReader classReader = new ClassReader(fileInputStream);
ClassWriter cw = new ClassWriter(classReader, ClassWriter.COMPUTE_MAXS);
//忽略调试信息
ClassNode classNode = new ClassNode(org.objectweb.asm.Opcodes.ASM5);
classReader.accept(classNode, ClassReader.SKIP_DEBUG);
for (MethodNode methodNode:classNode.methods) {
System.out.println(methodNode.name);
}
classNode.accept(cw);
}
输出如下:
<init>
getAge
setAge
main
本文简单介绍了ASM的介绍,以及简单的使用,下一章会介绍如何去用ASM做一个可用的东西,以及java的instrument机制。