簡介:ASM 是一個 Java 字節碼操控框架。它能被用來動態生成類或者增強既有類的功能。ASM 可以直接產生二進制 class 文件,也可以在類被加載入 Java 虛擬機之前動態改變類行爲。Java class 被存儲在嚴格格式定義的 .class 文件裏,這些類文件擁有足夠的元數據來解析類中的所有元素:類名稱、方法、屬性以及 Java 字節碼(指令)。ASM 從類文件中讀入信息後,能夠改變類行爲,分析類信息,甚至能夠根據用戶要求生成新類。
Asm架構整體都圍繞着兩個接口,即ClassVisitor 和 CodeVisitor,它們能訪問每個類的方法,成員變量,包含在每個方法中的字節碼指令。ClassReader用來讀取class文件;ClassWriter類用來寫生成的Class文件。
爲了修改已經存在的class,你必須使用分析class文件的ClassReader,類的修正器和寫class文件的ClassWriter。 類的修正器就是一個ClassVisitor,它可以委派一部分工作到其他的ClassVisitor,但是爲了實現預期的修改步驟,它將改變一些參數的 值,或者調用一些其他方法。爲了比較容易的實現這種類的修正器,ASM提供了一個ClassAdapter和CodeAdapter,這兩個適配器類分別 實現了ClassVistor和CodeVistor接口。
ASM官網:http://asm.ow2.org/
ASM最新版本是4.0 下載地址:http://forge.ow2.org/projects/asm/
學習ASM最好能下載ASM的一個eclipse的插件,這個插件能看你當前類如果用ASM來生成代碼應該怎樣寫。插件下載地址:http://download.forge.objectweb.org/eclipse-update/
下面用ASM寫一個HelloWorld,體驗一下哈。
import java.io.FileOutputStream;
import java.lang.reflect.Method;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
public class HelloWorld extends ClassLoader {
/**
* 如何使用ASM動態生成一個類,並打印出HelloWorld!
*
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
// 創建一個ClassWriter來寫示例類,這個類繼承Object
ClassWriter cw = new ClassWriter(0);
/*
* 第一個參數:JDK版本
* 第二個參數:這個類的訪問標記
* 第三個參數:這個類的名字
* 第四個參數:這個類的簽名,當這個類沒有繼承或者實現一個接口的時候可以爲空。
* 第五個參數:當前類父類的名字 接口的父類是Object當該類是Object的時候爲空
* 第六個參數:接口的名字 可以爲空
*/
cw.visit(Opcodes.V1_6, Opcodes.ACC_PUBLIC, "Example", null, "java/lang/Object", null);
/*
* 創建一個寫默認構造器的MethodWriter
* 第一個參數:方法的訪問標記
* 第二個參數:方法名稱
* 第三個參數:方法的描述符號
* 第四個參數:方法簽名(當方法參數 返回類型 以及異常沒有用到類屬性的時候可以爲空)
* 第⑤個參數:異常名稱
*/
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
// 壓入this變量
mv.visitVarInsn(Opcodes.ALOAD, 0);
// 執行父類構造器
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
mv.visitInsn(Opcodes.RETURN);
// 這段代碼使用最多一個堆元素 和一個 局部變量
mv.visitMaxs(1, 1);
mv.visitEnd();
// 創建一個main方法的MethodWriter
mv = cw.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
// 調用System類的PrintStream類的out
mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
// 壓入"Hello World!" 常量
mv.visitLdcInsn("Hello World!");
// 執行定義在PrintStream中的println方法
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V");
mv.visitInsn(Opcodes.RETURN);
// 使用兩個堆和兩個局部變量
mv.visitMaxs(2, 2);
// 獲取Example類的字節碼並且動態加載它。
byte[] code = cw.toByteArray();
FileOutputStream fos = new FileOutputStream("Example.class");
fos.write(code);
fos.close();
HelloWorld loader = new HelloWorld();
Class<?> exampleClass = loader.defineClass("Example", code, 0, code.length);
// 使用動態生成的類打印HelloWorld
Method method = exampleClass.getMethods()[0];
method.invoke(null, new Object[] { null });
}
}