首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

AOP@Work: AspectJ 5 搶鮮看——Java 5 全面支援與 @AspectJ 新風格

     編輯:Java 相關知識

AspectJ 5(M2 里程碑)核心聚焦:完整支援 Java 5 新語言特性(註解、泛型、枚舉、自動裝箱、新 for 迴圈),並引入兩大里程碑特性——基於註解的 @AspectJ 開發風格與大幅強化的載入時織入(LTW)。兩者皆源自 AspectJ 與 AspectWerkz 2005 年初的專案合併。本文涵蓋編譯器配置、Java 5 特性在切面中的實戰、泛型擦除對切點匹配的影響、@AspectJ 語法詳解、LTW 配置與代理選項。

編譯 Java 5 專案:ajc 編譯器與 AJDT 配置

  • ajc 支援 -1.3/-1.4/-1.5 相容等級,預設 5.0 原始碼與目標 VM。
  • 命令列:ajc -1.5 MyAspect.aj 明確指定 Java 5 模式;-1.4 回退 1.4。
  • Eclipse + AJDT:專案屬性 → Java Compiler → Compiler compliance level 設 5.0,並更新 JRE 至 Java 5。

圖 1:Eclipse 中指定 5.0 相容等級

切面中運用 Java 5 特性:LifecycleManager 範例

public aspect LifecycleManager {
  // Enum 定義狀態機
  public enum State { INITIAL, INITIALIZING, INITIALIZED, 
                      STARTING, STARTED, STOPPING, 
                      TERMINATING, TERMINATED, BROKEN; }

  // 型別間宣告:所有 @ManagedComponent 型別實作 Lifecycle 介面
  declare parents: @ManagedComponent * implements Lifecycle;

  // 介面預設實作:使用 Enum、泛型 List<LifecycleObserver>、新 for 迴圈
  private State Lifecycle.state = State.INITIAL;
  private List<LifecycleObserver> Lifecycle.observers = 
    new ArrayList<LifecycleObserver>();

  public void Lifecycle.addObserver(LifecycleObserver o) { 
    observers.add(o); 
  }
  // ... 其他介面方法預設實作

  // 切點捕獲生命週期事件
  pointcut initializing(Lifecycle l) 
    : execution(* Lifecycle.initialize(..)) && this(l);
  // starting, stopping, terminating 同理

  // Before 通知驗證狀態
  before(Lifecycle c) : initializing(c) {
    if (c.state != State.INITIAL) 
      throw new IllegalStateException("Can only initialize from INITIAL");
    c.state = State.INITIALIZING;
  }

  // After returning 通知更新狀態並通知觀察者(新 for 迴圈)
  after(Lifecycle c) returning : initializing(c) {
    c.state = State.INITIALIZED;
    for (LifecycleObserver o : c.observers)
      o.componentInitialized(c);
  }

  // After throwing 通知處理運行時異常,轉入 BROKEN 狀態
  after(Lifecycle c) throwing(RuntimeException e) 
    : execution(* *(..)) && this(c) {
    c.state = State.BROKEN;
    for (LifecycleObserver o : c.observers)
      o.componentBroken(c);
  }
}

要點:切面內使用 Java 5 特性與普通類別完全一致,包括泛型型別間宣告、Enum、新 for 迴圈、自動裝箱。

基於註解的連接點匹配

匹配帶註解的方法執行/呼叫

// EJB 3.0 風格 @Tx 註解
@Tx(TxType.REQUIRED) void credit(Money amount) { ... }

// 切點:匹配所有帶 @Tx 的方法執行
pointcut transactionalMethodExecution() 
  : execution(@Tx * *(..));

// 綁定註解實例取得屬性值(需 RUNTIME 保留)
pointcut transactionalMethodExecution(Tx tx) 
  : execution(* *(..)) && @annotation(tx);

Object around(Tx tx) : transactionalMethodExecution(tx) {
  TxType type = tx.value(); // 存取屬性
  // 交易邏輯...
  return proceed(tx);
}

欄位與型別層級註解匹配

// 欄位存取
pointcut classifiedAction() 
  : get(@ClassifiedData * *) || set(@ClassifiedData * *);

// 綁定欄位註解
pointcut classifiedAction(ClassifiedData cd) 
  : (get(* *) || set(* *)) && @annotation(cd);

// 型別註解組合匹配:@Stateless 或 @Stateful 的 Session Bean
pointcut txInSessionBean(Tx tx) 
  : execution(* *(..)) && @annotation(tx) 
    && within(@(Stateless || Stateful) *);

新增切點指示符擴充註解匹配維度

  • @withincode:執行碼所在成員帶指定註解
  • @within:連接點所在型別帶指定註解
  • @this、@target、@args:this/target/參數帶指定註解

泛型與切點匹配:擦除帶來的挑戰與應對

靜態簽名匹配:型別參數為簽名一部分

void process(List<Number> n);
void process(List<? extends Number> n);
void process(List<?> n);  // 三者簽名互異

execution(* process(List<Number>))      // 僅匹配第一個
execution(* process(List<*>))          // 匹配全部三個
execution(* process(List<Number+>))    // 僅匹配第一個(Number 及子類)
// 注意:List<? extends Number> 不匹配 Number+ 模式

運行時型別匹配:擦除導致資訊遺失

Java 5 泛型採擦除實作,執行期 List<Number> 僅保留為原始型別 List。AspectJ 面對 args(List<Number>) 這類切點:

  • args(List<? extends Number>):靜態保證匹配
  • args(List<String>):靜態保證不匹配
  • args(List<Number>):可能匹配(執行期實際可能為 List<Double> 等),編譯器發出 unchecked 警告,視為匹配並插入執行期 instanceof 檢查(僅檢查原始型別 List)。

可用 @SuppressAjWarnings("unchecked") 抑制切點匹配警告,類似 @SuppressWarnings。

泛型型別的執行連接點

單一泛型類別 DataBucket<T> 所有實例化共享同一 getData() 執行連接點(回傳型別參數 T)。切點可寫為:

execution<T>(T DataBucket<T>.getData())

@AspectJ 註解風格:純 Java 5 語法寫切面

合併成果:AspectJ 5 提供雙風格,單一語言、單一語義、單一織入器,風格可混合互操作。

程式碼風格 vs @AspectJ 風格對照

// 程式碼風格
public aspect TransactionManager { ... }

// @AspectJ 風格
@Aspect
public class TransactionManager { ... }

切點與通知的註解映射

@Aspect
public class LifecycleManager {
  // 切點:void 方法 + @Pointcut 含運算式字串
  @Pointcut("execution(* Lifecycle.initialize(..)) && this(l)")
  void initializing(Lifecycle l) {}

  @Pointcut("execution(* Lifecycle.start(..)) && this(l)")
  void starting(Lifecycle l) {}

  // Before 通知
  @Before("initializing(managedComponent)")
  public void moveToInitializingState(Lifecycle managedComponent) { ... }

  // After returning 通知
  @AfterReturning("initializing(managedComponent)")
  public void moveToInitializedState(Lifecycle managedComponent) { ... }

  // Around 通知(需回傳值)
  @Around("transactionalMethodExecution(tx)")
  public Object manageTransaction(Tx tx) { ... }

  // 型別間宣告:@DeclareParents
  @DeclareParents("@ManagedComponent *")
  static class DefaultLifecycleImpl implements Lifecycle { ... }
}

限制:切點運算式中型別需完整限定名(import 不對織入器可見);通知方法必須 public、回傳 void(around 除外);標準 javac 編譯時不支援 declare soft 等需編譯期支援的構造。

載入時織入(LTW)大幅簡化:META-INF/aop.xml

AspectJ 1.x 需自訂類別載入器或 aj 腳本啟動;AspectJ 5 採 JVMTI Agent(-javaagent)+ classpath 下的 META-INF/aop.xml,零侵入式整合任何 Java EE 環境。

aop.xml 結構

<aspectj>
  <aspects>
    <aspect name="com.MyAspect"/>
    <concrete-aspect name="com.xyz.tracing.MyTracing"
      extends="tracing.AbstractTracing">
      <pointcut name="tracingScope" expression="within(com.xyz..*)"/>
    </concrete-aspect>
  </aspects>
  <weaver options="-XlazyTjp">
    <include within="com.xyz..*"/>
  </weaver>
</aspectj>
  • <aspects>:宣告織入切面(現有或內聯具體化抽象切面)
  • <weaver>:織入器選項與 include/exclude 控制織入範圍
  • 多個 aop.xml 內容自動聚合

啟動選項

  • 標準 JVMTI(Java 5+ 所有 VM):-javaagent:aspectjweaver.jar
  • JRockit:-Xmanagement:class=org.aspectj.weaver.tools.JRockitWeavingAgent
  • WebLogic、WebSphere、JBoss、Tomcat 等容器皆可直接在啟動腳本加入 javaagent 參數啟用 LTW

其他值得關注的新特性

  • pertypewithin 實例化模型:每個型別在特定封裝內一個切面實例
  • 反射 API 查詢切面型別:執行期檢測切面屬性
  • declare soft 修訂:更靈活的受檢例外轉非受檢
  • 相容性、織入效能提升

結語

AspectJ 5 以 Java 5 全支援為基石,配合 @AspectJ 風格降低語言擴展門檻、LTW 簡化部署,標誌著 AOP 邁入主流開發的關鍵里程碑。開發者可選擇熟悉的風格(程式碼或註解),享受完整靜態檢查、成熟 IDE 支持與零侵入部署的雙重紅利。詳細特性請參閱 AspectJ 5 Developers Guide。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved