訪問者模式和 ASM

之前三篇文章介紹了 .class 文件的結(jié)構(gòu)菱涤、JVM 對 .class 文件加載以及在 JVM 中是怎么執(zhí)行程序的,接下來的文章會介紹 ASM 的使用劫哼,ASM 是運(yùn)用訪問者模式設(shè)計的皂吮,本篇文章就介紹一下訪問者模式的概念以及其在 ASM 中的應(yīng)用。


catalog.png

一. 概述 & 定義

  1. 定義:封裝某些作用于某種數(shù)據(jù)結(jié)構(gòu)中各元素的操作厂镇,它可以在不改變數(shù)據(jù)結(jié)構(gòu)的前提下定義作用于這些數(shù)據(jù)元素的新的操作
  2. 意圖:主要將數(shù)據(jù)結(jié)構(gòu)和數(shù)據(jù)操作分離
  3. 主要解決:穩(wěn)定的數(shù)據(jù)結(jié)構(gòu)和易變的操作的解耦
  4. 適用場景:
    • 假如一個對象中存在著一些與本對象不相干(或者關(guān)系較弱)的操作,可以使用訪問者模式把這些操作封裝到訪問者中去左刽,這樣便避免了這些不相干的操作污染這個對象。
    • 假如一組對象中酌媒,存在著相似的操作欠痴,可以將這些相似的操作封裝到訪問者中去,這樣便避免了出現(xiàn)大量重復(fù)的代碼
    • 訪問者模式適用于對功能已經(jīng)確定的項(xiàng)目進(jìn)行重構(gòu)的時候適用秒咨,因?yàn)楣δ芤呀?jīng)確定喇辽,元素類的數(shù)據(jù)結(jié)構(gòu)也基本不會變了;如果是一個新的正在開發(fā)中的項(xiàng)目雨席,在訪問者模式中菩咨,每一個元素類都有它對應(yīng)的處理方法,每增加一個元素類都需要修改訪問者類,修改起來相當(dāng)麻煩抽米。

二. 示例

如果老師教學(xué)反饋得分大于等于85分特占、學(xué)生成績大于等于90分,則可以入選成績優(yōu)秀獎云茸;如果老師論文數(shù)目大于8是目、學(xué)生論文數(shù)目大于2,則可以入選科研優(yōu)秀獎标捺。

在這個例子中懊纳,老師和學(xué)生就是Element,他們的數(shù)據(jù)結(jié)構(gòu)穩(wěn)定不變亡容。從上面的描述中嗤疯,我們發(fā)現(xiàn),對數(shù)據(jù)結(jié)構(gòu)的操作是多變的闺兢,一會兒評選成績身弊,一會兒評選科研,這樣就適合使用訪問者模式來分離數(shù)據(jù)結(jié)構(gòu)和操作列敲。

2.1 創(chuàng)建抽象元素

public interface Element {
    void accept(Visitor visitor);
}

2.2 創(chuàng)建具體元素

創(chuàng)建兩個具體元素 Student 和 Teacher阱佛,分別實(shí)現(xiàn) Element 接口

public class Student implements Element {
    private String name;
    private int grade;
    private int paperCount;

    public Student(String name, int grade, int paperCount) {
        this.name = name;
        this.grade = grade;
        this.paperCount = paperCount;
    }

    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
        
    ......
      
}
public class Teacher implements Element {
    private String name;
    private int score;
    private int paperCount;

    public Teacher(String name, int score, int paperCount) {
        this.name = name;
        this.score = score;
        this.paperCount = paperCount;
    }

    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }

        ......
      
}

2.3 創(chuàng)建抽象訪問者

public interface Visitor {

    void visit(Student student);

    void visit(Teacher teacher);
}

2.4 創(chuàng)建具體訪問者

創(chuàng)建一個根據(jù)分?jǐn)?shù)評比的具體訪問者 GradeSelection,實(shí)現(xiàn) Visitor 接口

public class GradeSelection implements Visitor {

    @Override
    public void visit(Student student) {
        if (student != null && student.getGrade() >= 90) {
            System.out.println(student.getName() + "的分?jǐn)?shù)是" + student.getGrade() + "戴而,榮獲了成績優(yōu)秀獎凑术。");
        }
    }

    @Override
    public void visit(Teacher teacher) {
        if (teacher != null && teacher.getScore() >= 85) {
            System.out.println(teacher.getName() + "的分?jǐn)?shù)是" + teacher.getScore() + ",榮獲了成績優(yōu)秀獎所意。");
        }
    }
}

2.5 訪問者代碼調(diào)用

public class VisitorClient {

    public static void main(String[] args) {
        Element element = new Student("lijiankun24", 90, 3);

        Visitor visitor = new GradeSelection();
        element.accept(visitor);
    }
}

上述代碼即是一個簡單的訪問者模式的示例代碼淮逊,輸出如下所示:


visitor.png

上述代碼可以分為三步:
1. 創(chuàng)建一個元素類的對象
2. 創(chuàng)建一個訪問類的對象
3. 元素對象通過 Element#accept(Visitor visitor) 方法傳入訪問者對象

三. ASM 中的訪問者模式

ASM 庫是 Visitor 模式的典型應(yīng)用。

3.1 ASM 中幾個重要的類

在 ASM 庫中存在以下幾個重要的類:

  • ClassReader:它將字節(jié)數(shù)組或者 class 文件讀入到內(nèi)存當(dāng)中扶踊,并以樹的數(shù)據(jù)結(jié)構(gòu)表示泄鹏,樹中的一個節(jié)點(diǎn)代表著 class 文件中的某個區(qū)域⊙砗模可以將 ClassReader 看作是 Visitor 模式中的訪問者的實(shí)現(xiàn)類
  • ClassVisitor(抽象類):ClassReader 對象創(chuàng)建之后备籽,調(diào)用 ClassReader#accept() 方法,傳入一個 ClassVisitor 對象分井。在 ClassReader 中遍歷樹結(jié)構(gòu)的不同節(jié)點(diǎn)時會調(diào)用 ClassVisitor 對象中不同的 visit() 方法车猬,從而實(shí)現(xiàn)對字節(jié)碼的修改。在 ClassVisitor 中的一些訪問會產(chǎn)生子過程尺锚,比如 visitMethod 會產(chǎn)生 MethodVisitor 的調(diào)用珠闰,visitField 會產(chǎn)生對 FieldVisitor 的調(diào)用,用戶也可以對這些 Visitor 進(jìn)行自己的實(shí)現(xiàn)瘫辩,從而達(dá)到對這些子節(jié)點(diǎn)的字節(jié)碼的訪問和修改伏嗜。
    在 ASM 的訪問者模式中坛悉,用戶還可以提供多種不同操作的 ClassVisitor 的實(shí)現(xiàn),并以責(zé)任鏈的模式提供給 ClassReader 來使用承绸,而 ClassReader 只需要 accept 責(zé)任鏈中的頭節(jié)點(diǎn)處的 ClassVisitor裸影。
  • ClassWriter:ClassWriter 是 ClassVisitor 的實(shí)現(xiàn)類,它是生成字節(jié)碼的工具類八酒,它一般是責(zé)任鏈中的最后一個節(jié)點(diǎn)空民,其之前的每一個 ClassVisitor 都是致力于對原始字節(jié)碼做修改,而 ClassWriter 的操作則是老實(shí)得把每一個節(jié)點(diǎn)修改后的字節(jié)碼輸出為字節(jié)數(shù)組羞迷。

3.2 ASM 的工作流程

ASM 大致的工作流程是:

  1. ClassReader 讀取字節(jié)碼到內(nèi)存中界轩,生成用于表示該字節(jié)碼的內(nèi)部表示的樹,ClassReader 對應(yīng)于訪問者模式中的元素
  2. 組裝 ClassVisitor 責(zé)任鏈衔瓮,這一系列 ClassVisitor 完成了對字節(jié)碼一系列不同的字節(jié)碼修改工作浊猾,對應(yīng)于訪問者模式中的訪問者 Visitor
  3. 然后調(diào)用 ClassReader#accept() 方法,傳入 ClassVisitor 對象热鞍,此 ClassVisitor 是責(zé)任鏈的頭結(jié)點(diǎn)葫慎,經(jīng)過責(zé)任鏈中每一個 ClassVisitor 的對已加載進(jìn)內(nèi)存的字節(jié)碼的樹結(jié)構(gòu)上的每個節(jié)點(diǎn)的訪問和修改
  4. 最后,在責(zé)任鏈的末端薇宠,調(diào)用 ClassWriter 這個 visitor 進(jìn)行修改后的字節(jié)碼的輸出工作
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末偷办,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子澄港,更是在濱河造成了極大的恐慌椒涯,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件回梧,死亡現(xiàn)場離奇詭異废岂,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)狱意,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進(jìn)店門湖苞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人详囤,你說我怎么就攤上這事财骨。” “怎么了纬纪?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵蚓再,是天一觀的道長。 經(jīng)常有香客問我包各,道長,這世上最難降的妖魔是什么靶庙? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任问畅,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘护姆。我一直安慰自己矾端,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布卵皂。 她就那樣靜靜地躺著秩铆,像睡著了一般。 火紅的嫁衣襯著肌膚如雪灯变。 梳的紋絲不亂的頭發(fā)上殴玛,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天,我揣著相機(jī)與錄音添祸,去河邊找鬼滚粟。 笑死,一個胖子當(dāng)著我的面吹牛刃泌,可吹牛的內(nèi)容都是我干的凡壤。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼耙替,長吁一口氣:“原來是場噩夢啊……” “哼亚侠!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起俗扇,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤硝烂,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后狐援,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體钢坦,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年啥酱,在試婚紗的時候發(fā)現(xiàn)自己被綠了爹凹。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡镶殷,死狀恐怖禾酱,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情绘趋,我是刑警寧澤颤陶,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站陷遮,受9級特大地震影響滓走,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜帽馋,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一搅方、第九天 我趴在偏房一處隱蔽的房頂上張望比吭。 院中可真熱鬧,春花似錦姨涡、人聲如沸衩藤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽赏表。三九已至,卻和暖如春匈仗,著一層夾襖步出監(jiān)牢的瞬間瓢剿,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工锚沸, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留跋选,地道東北人。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓哗蜈,卻偏偏與公主長得像前标,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子距潘,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內(nèi)容