asm 與 cglib(整理的)


參考博客地址

http://www.oseye.net/user/kevin/blog/304#top

http://www.blogjava.net/vanadies10/archive/2011/02/23/344899.html

http://llying.iteye.com/blog/220452

http://www.cnblogs.com/liuling/archive/2013/05/21/CGlib-AOP.html

1 asm簡介

ASM是一個Java字節碼操縱框架,它能被用來動態生成類或者增強既有類的功能。ASM可以直接產生二進制class文件,也可以在類被加載入Java虛擬機之前動態改變類行為。Java class被存儲在嚴格格式定義的.class文件里,這些類文件擁有足夠的元數據來解析類中的所有元素:類名稱、方法、屬性以及 Java 字節碼(指令)。ASM從類文件中讀入信息后,能夠改變類行為,分析類信息,甚至能夠根據用戶要求生成新類。

目前許多框架如cglib、Hibernate、Spring都直接或間接地使用ASM操作字節碼,有些語言如Jython、JRuby、Groovy也是如此。而類ASM字節碼工具還有:

  1. BCEL:Byte Code Engineering Library (BCEL),這是Apache Software Foundation 的Jakarta 項目的一部分。BCEL是 Java classworking 最廣泛使用的一種框架,它可以讓您深入 JVM 匯編語言進行類操作的細節。BCEL與Javassist 有不同的處理字節碼方法,BCEL在實際的JVM 指令層次上進行操作(BCEL擁有豐富的JVM 指令級支持)而Javassist 所強調的源代碼級別的工作。
  2. JBET:通過JBET(Java Binary Enhancement Tool )的API可對Class文件進行分解,重新組合,或被編輯。JBET也可以創建新的Class文件。JBET用一種結構化的方式來展現Javabinary (.class)文件的內容,並且可以很容易的進行修改。
  3. Javassist:Javassist是一個開源的分析、編輯和創建Java字節碼的類庫。是由東京技術學院的數學和計算機科學系的 Shigeru Chiba 所創建的。它已加入了開放源代碼JBoss 應用服務器項目,通過使用Javassist對字節碼操作為JBoss實現動態AOP框架。
  4. cglib:是一個強大的,高性能,高質量的Code生成類庫。它可以在運行期擴展Java類與實現Java接口,cglib封裝了asm,可以在運行期動態生成新的 class,Hibernate和Spring都用到過它。cglib用於AOP,jdk中的proxy必須基於接口,cglib卻沒有這個限制。

而ASM與cglib、serp和BCEL相比,ASM有以下的優點 :

  • ASM 具有簡單、設計良好的 API,這些 API 易於使用;
  • ASM 有非常良好的開發文檔,以及可以幫助簡化開發的 Eclipse 插件;
  • ASM 支持 Java 6(ASM3)、Java7(ASM4)、Java(ASM5);
  • ASM 很小、很快、很健壯;
  • ASM 有很大的用戶群,可以幫助新手解決開發過程中遇到的問題;
  • ASM 的開源許可可以讓你幾乎以任何方式使用它;

2 asm jar包簡介

  在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,都是基於這個核心的擴展。

 

3  Java字節碼文件

所謂 Java 字節碼文件,就是通常用 javac 編譯器產生的 .class 文件。這些文件具有嚴格定義的格式。為了更好的理解 ASM,首先對 Java 字節碼文件格式作一點簡單的介紹。Java 源文件經過 javac 編譯器編譯之后,將會生成對應的二進制文件(如下圖所示)。每個合法的 Java 字節碼文件都具備精確的定義,而正是這種精確的定義,才使得 Java 虛擬機得以正確讀取和解釋所有的 Java 字節碼文件。

Java 字節碼文件是 8 位字節的二進制流。數據項按順序存儲在 class 文件中,相鄰的項之間沒有間隔,這使得 class 文件變得緊湊,減少存儲空間。在 Java 字節碼文件中包含了許多大小不同的項,由於每一項的結構都有嚴格規定,這使得 class 文件能夠從頭到尾被順利地解析。下面讓我們來看一下 Java 字節碼文件的內部結構,以便對此有個大致的認識。

例如,一個最簡單的 Hello World 程序:

  1. public class HelloWorld {
  2. public static void main(String[] args) {
  3. System.out.println("Hello world");
  4. }
  5. }

從上圖中可以看到,一個 Java 字節碼文件大致可以歸為 10 個項:

  • Magic:該項存放了一個 Java 字節碼文件的魔數(magic number)和版本信息。一個 Java 字節碼文件的前 4 個字節被稱為它的魔數。每個正確的 Java 字節碼文件都是以 0xCAFEBABE 開頭的,這樣保證了 Java 虛擬機能很輕松的分辨出 Java 文件和非 Java 文件。
  • Version:該項存放了 Java 字節碼文件的版本信息,它對於一個 Java 文件具有重要的意義。因為 Java 技術一直在發展,所以字節碼文件的格式也處在不斷變化之中。字節碼文件的版本信息讓虛擬機知道如何去讀取並處理該字節碼文件。
  • Constant Pool:該項存放了類中各種文字字符串、類名、方法名和接口名稱、final 變量以及對外部類的引用信息等常量。虛擬機必須為每一個被裝載的類維護一個常量池,常量池中存儲了相應類型所用到的所有類型、字段和方法的符號引用,因此它在 Java 的動態鏈接中起到了核心的作用。常量池的大小平均占到了整個類大小的 60% 左右。
  • Access_flag:該項指明了該文件中定義的是類還是接口(一個 class 文件中只能有一個類或接口),同時還指名了類或接口的訪問標志,如 public,private, abstract 等信息。
  • This Class:指向表示該類全限定名稱的字符串常量的指針。
  • Super Class:指向表示父類全限定名稱的字符串常量的指針。
  • Interfaces:一個指針數組,存放了該類或父類實現的所有接口名稱的字符串常量的指針。以上三項所指向的常量,特別是前兩項,在我們用 ASM 從已有類派生新類時一般需要修改:將類名稱改為子類名稱;將父類改為派生前的類名稱;如果有必要,增加新的實現接口。
  • Fields:該項對類或接口中聲明的字段進行了細致的描述。需要注意的是,fields 列表中僅列出了本類或接口中的字段,並不包括從超類和父接口繼承而來的字段。
  • Methods:該項對類或接口中聲明的方法進行了細致的描述。例如方法的名稱、參數和返回值類型等。需要注意的是,methods 列表里僅存放了本類或本接口中的方法,並不包括從超類和父接口繼承而來的方法。使用 ASM 進行 AOP 編程,通常是通過調整 Method 中的指令來實現的。
  • Class attributes:該項存放了在該文件中類或接口所定義的屬性的基本信息。

事實上,使用 ASM 動態生成類,不需要像早年的 class hacker 一樣,熟知 class 文件的每一段,以及它們的功能、長度、偏移量以及編碼方式。ASM 會給我們照顧好這一切的,我們只要告訴 ASM 要改動什么就可以了 —— 當然,我們首先得知道要改什么:對字節碼文件格式了解的越多,我們就能更好地使用 ASM 這個利器。

ASM編程模型

ASM 提供了兩種編程模型:

  • Core API,提供了基於事件形式的編程模型。該模型不需要一次性將整個類的結構讀取到內存中,因此這種方式更快,需要更少的內存。但這種編程方式難度較大。
  • Tree API,提供了基於樹形的編程模型。該模型需要一次性將一個類的完整結構全部讀取到內存當中,所以這種方法需要更多的內存。這種編程方式較簡單。

Core API 中操縱字節碼的功能基於 ClassVisitor 接口。這個接口中的每個方法對應了 class 文件中的每一項。Class 文件中的簡單項的訪問使用一個單獨的方法,方法參數描述了這個項的內容。而那些具有任意長度和復雜度的項,使用另外一類方法,這類方法會返回一個輔助的 Visitor 接口,通過這些輔助接口的對象來完成具體內容的訪問。例如 visitField 方法和 visitMethod 方法,分別返回 FieldVisitor 和 MethodVisitor 接口的對象。

ASM 提供了三個基於 ClassVisitor 接口的類來實現 class 文件的生成和轉換:

  • ClassReader:ClassReader 解析一個類的 class 字節碼,該類的 accept 方法接受一個 ClassVisitor 的對象,在 accept 方法中,會按上文描述的順序逐個調用 ClassVisitor 對象的方法。它可以被看做事件的生產者。
  • ClassAdapter:ClassAdapter 是 ClassVisitor 的實現類。它的構造方法中需要一個 ClassVisitor 對象,並保存為字段 protected ClassVisitor cv。在它的實現中,每個方法都是原封不動的直接調用 cv 的對應方法,並傳遞同樣的參數。可以通過繼承 ClassAdapter 並修改其中的部分方法達到過濾的作用。它可以看做是事件的過濾器。
  • ClassWriter:ClassWriter 也是 ClassVisitor 的實現類。ClassWriter 可以用來以二進制的方式創建一個類的字節碼。對於 ClassWriter 的每個方法的調用會創建類的相應部分。例如:調用 visit 方法就是創建一個類的聲明部分,每調用一次 visitMethod 方法就會在這個類中創建一個新的方法。在調用 visitEnd 方法后即表明該類的創建已經完成。它最終生成一個字節數組,這個字節數組中包含了一個類的 class 文件的完整字節碼內容 。可以通過 toByteArray 方法獲取生成的字節數組。ClassWriter 可以看做事件的消費者

5 ASMifer 工具

  直接編碼ASM其實對於新手來說是很困難的事,但幸運的是ASM給我們提供了ASMifer工具。一般我們會使用ASM的ASMifer工具生成ASM結構來對比,使用命令:

  1. java org.objectweb.asm.util.ASMifier net.oseye.demoasm.HelloWorld

6 例子:參考地址

  http://www.blogjava.net/vanadies10/archive/2011/02/23/344899.html

  http://llying.iteye.com/blog/220452

  http://www.cnblogs.com/liuling/archive/2013/05/21/CGlib-AOP.html

  代碼 : 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM