什么是 ASM?
ASM 是一個 Java 字節(jié)碼操控框架。它能被用來動態(tài)生成類或者增強(qiáng)既有類的功能。ASM 可以直接產(chǎn)生二進(jìn)制 class 文件,也可以在類被加載入 Java 虛擬機(jī)之前動態(tài)改變類行為。Java class 被存儲在嚴(yán)格格式定義的 .class 文件里,這些類文件擁有足夠的元數(shù)據(jù)來解析類中的所有元素:類名稱、方法、屬性以及 Java 字節(jié)碼(指令)。ASM 從類文件中讀入信息后,能夠改變類行為,分析類信息,甚至能夠根據(jù)用戶要求生成新類。
與 BCEL 和 SERL 不同,ASM 提供了更為現(xiàn)代的編程模型。對于 ASM 來說,Java class 被描述為一棵樹;使用 “Visitor” 模式遍歷整個二進(jìn)制結(jié)構(gòu);事件驅(qū)動的處理方式使得用戶只需要關(guān)注于對其編程有意義的部分,而不必了解 Java 類文件格式的所有細(xì)節(jié):ASM 框架提供了默認(rèn)的 “response taker”處理這一切。
為什么要動態(tài)生成 Java 類?
動態(tài)生成 Java 類與 AOP 密切相關(guān)的。AOP 的初衷在于軟件設(shè)計(jì)世界中存在這么一類代碼,零散而又耦合:零散是由于一些公有的功能(諸如的 log 例子)分散在所有模塊之中;同時改變 log 功能又會影響到所有的模塊。出現(xiàn)這樣的缺陷,很大程度上是由于傳統(tǒng)的 面向?qū)ο缶幊套⒅匾岳^承關(guān)系為代表的“縱向”關(guān)系,而對于擁有相同功能或者說方面 (Aspect)的模塊之間的“橫向”關(guān)系不能很好地表達(dá)。例如,目前有一個既有的銀行管理系統(tǒng),包括 Bank、Customer、Aclearcase/" target="_blank" >ccount、Invoice 等對象,現(xiàn)在要加入一個安全檢查模塊, 對已有類的所有操作之前都必須進(jìn)行一次安全檢查。
圖 1. ASM – AOP
然而 Bank、Customer、Account、Invoice 是代表不同的事務(wù),派生自不同的父類,很難在高層上加入關(guān)于 Security Checker 的共有功能。對于沒有多繼承的 Java 來說,更是如此。傳統(tǒng)的解決方案是使用 Decorator 模式,它可以在一定程度上改善耦合,而功能仍舊是分散的 —— 每個需要 Security Checker 的類都必須要派生一個 Decorator,每個需要 Security Checker 的方法都要被包裝(wrap)。下面我們以 Account 類為例看一下 Decorator:
首先,我們有一個 SecurityChecker 類,其靜態(tài)方法 checkSecurity 執(zhí)行安全檢查功能:
public class SecurityChecker { public static void checkSecurity() { System.out.println("SecurityChecker.checkSecurity ..."); //TODO real security check } }
另一個是 Account 類:
public class Account { public void operation() { System.out.println("operation..."); //TODO real operation } }
若想對 operation 加入對 SecurityCheck.checkSecurity() 調(diào)用,標(biāo)準(zhǔn)的 Decorator 需要先定義一個 Account 類的接口:
public interface Account { void operation(); }
然后把原來的 Account 類定義為一個實(shí)現(xiàn)類:
public class AccountImpl extends Account{ public void operation() { System.out.println("operation..."); //TODO real operation } }
定義一個 Account 類的 Decorator,并包裝 operation 方法:
public class AccountWithSecurityCheck implements Account { private Account account; public AccountWithSecurityCheck (Account account) { this.account = account; } public void operation() { SecurityChecker.checkSecurity(); account.operation(); } }
在這個簡單的例子里,改造一個類的一個方法還好,如果是變動整個模塊,Decorator 很快會演化成另一個噩夢。動態(tài)改變 Java 類是要解決 AOP 的問題,提供一種得到系統(tǒng)支持的可編程的方法,自動化地生成或者增強(qiáng) Java 代碼。這種技術(shù)已經(jīng)廣泛應(yīng)用于新的 Java 框架內(nèi),如 Hibernate,Spring 等。