AspectJ 5(M2 里程碑)核心聚焦:完整支援 Java 5 新語言特性(註解、泛型、枚舉、自動裝箱、新 for 迴圈),並引入兩大里程碑特性——基於註解的 @AspectJ 開發風格與大幅強化的載入時織入(LTW)。兩者皆源自 AspectJ 與 AspectWerkz 2005 年初的專案合併。本文涵蓋編譯器配置、Java 5 特性在切面中的實戰、泛型擦除對切點匹配的影響、@AspectJ 語法詳解、LTW 配置與代理選項。
ajc 支援 -1.3/-1.4/-1.5 相容等級,預設 5.0 原始碼與目標 VM。ajc -1.5 MyAspect.aj 明確指定 Java 5 模式;-1.4 回退 1.4。
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 5 提供雙風格,單一語言、單一語義、單一織入器,風格可混合互操作。
// 程式碼風格
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 等需編譯期支援的構造。
AspectJ 1.x 需自訂類別載入器或 aj 腳本啟動;AspectJ 5 採 JVMTI Agent(-javaagent)+ classpath 下的 META-INF/aop.xml,零侵入式整合任何 Java EE 環境。
<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 內容自動聚合-javaagent:aspectjweaver.jar-Xmanagement:class=org.aspectj.weaver.tools.JRockitWeavingAgentAspectJ 5 以 Java 5 全支援為基石,配合 @AspectJ 風格降低語言擴展門檻、LTW 簡化部署,標誌著 AOP 邁入主流開發的關鍵里程碑。開發者可選擇熟悉的風格(程式碼或註解),享受完整靜態檢查、成熟 IDE 支持與零侵入部署的雙重紅利。詳細特性請參閱 AspectJ 5 Developers Guide。