設計模式 - ASM 中的訪問者模式

本文首發於本人的博客, 歡迎關注java

ASM 是一個 Java 字節碼操做和分析框架, 可直接用二進制的方式(無需編譯/反編譯)修改已存在的類或動態生成新的類.web

問題

要開發像 ASM 這樣的框架, 主要面臨如下幾個問題:框架

動態添加操做方法

既然是一個字節碼的操做和分析框架, 那麼就要提供對字節碼進行各類各樣的操做, 且容許用戶自定義操做類型. 如在 ASM 中, 除了內置對字節碼進行輸出和轉換等默認實現以外, 還要容許用戶對字節碼進行加/解密等各類自定義的操做, 對於公開的商業代碼這多是相當重要的.ide

何處安放操做方法

雖然構成一個類的字節碼的各個元素有弱的層次關係: 表明類的字節碼可能包含 N 個表明字段/方法/註解的字節碼, 表明字段/方法的字節碼又可能包含 N 個表明註解的字節碼. 對這些元素所進行的操做, 應該如何安放. 一種直接的方法是在表明每種字節碼元素的類中定義相應的方法, 如 print() 用於輸出, transform() 用於轉換, 但在字節碼元素的類層次結構中加入這些毫無聯繫的操做方法, 會使代碼變得難以理解和維護.this

訪問者模式

做用在多種類對象上的操做, 訪問者能夠在不修改其操做對象所屬類的狀況下, 增長對其操做方式.spa

ASM 源代碼中主要使用了訪問者模式來解決以上問題:設計

動態添加操做方法

以對類的字段的字節碼進行操做爲例, ASM 中定義了 org.objectweb.asm.FieldVisitor 用來表示對字段的操做:code

package org.objectweb.asm;

public abstract class FieldVisitor {
  // ...

  public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) {
    // ...
    return null;
  }

  public AnnotationVisitor visitTypeAnnotation( final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) {
    // ...
    return null;
  }

  public void visitAttribute(final Attribute attribute) {
    // ...
  }

  public void visitEnd() {
    // ...
  }
}
複製代碼

這樣一來, 當用戶須要新增對字段進行操做的時候, 只須要繼承 FieldVisitor, 實現相應的方法便可, 以 ASM 中打印字段字節碼的類 FieldWriter 爲例:orm

package org.objectweb.asm;

final class FieldWriter extends FieldVisitor {
  // ...

  @Override
  public void visitAttribute(final Attribute attribute) {
    // Store the attributes in the <i>reverse</i> order of their visit by this method.
    attribute.nextAttribute = firstAttribute;
    firstAttribute = attribute;
  }

  void putFieldInfo(final ByteVector output) {
    // ...
  }

  // ...
}
複製代碼

FieldWriter 每訪問到一個字段, 就在內部保存該字段(按訪問順序逆序), 最終調用 putFieldInfo 將全部字段信息打印到 ByteVector 中.對象

其中 org.objectweb.asm.FieldVisitor#visitAttribute 方法是對類屬性的操做, Attribute 自己的設計並無加入如 print, transform 等方法, 而是將對 Attribute 的操做方法被抽離至 FieldVisitor 中, 不然就沒法知足"動態添加操做方法"的要求了.

經過應用訪問者模式, ASM 將對字節碼操做的方法從封裝了字節碼的類中抽離, 避免無關的方法污染類的設計.

何處安放操做方法

從上面的代碼能夠看出, ASM 並無對各類類元素的字節碼定義相應的類, 而且將對這些元素的操做直接放置在對應的訪問者 (Visitor) 中, 這正是訪問者模式典型應用: 統一管理對類層級中各類類的操做方法.

什麼時候使用訪問者模式

對象層次結構對應類層次結構差別很大

一個類包含 N 個註解, 字段和方法, 用於表示註解, 字段和方法的類型差別比較大, 可是對這些類型的操做須要相互結合, 才能對一個完整的類字節碼進行操做.

類中須要添加與類自己毫無關係的方法

表明字節碼的類和對其進行的操做毫無關係, 換句話說, 一個類自己的字節碼與別人如何操做它無關, 這些方法應該被抽離到訪問對象中, 而不是強行定義在表明字節碼的類中.

相關文章
相關標籤/搜索