Spring 透過 AOP 為普通 Java 類(POJO)提供聲明式事務管理,避免 EJB 重量級容器,適用於大多數 Web 應用。雖不支援跨資源管理器分佈式事務(JTA 需額外配置),但單數據源場景足夠。
Spring 自動選擇,亦可強制 proxy-target-class="true" 全用 CGLIB。
| 類型 | 切入點 | 返回值 | 異常行為 | 典型用途 |
|---|---|---|---|---|
| Around | 方法執行前後 | 可攔截/修改 | 可阻止繼續執行 | 事務邊界、性能監控、緩存 |
| Before | 方法執行前 | 無 | 拋出異常則阻止目標方法 | 權限檢查、日誌記錄 |
| After | 方法執行後 | 無 | 不影響目標方法返回 | 資源清理、統計 |
| Introduction | 類級別 | 動態新增方法/介面 | 無 | 混入審計字段、實現新介面 |
package com.osiris.springaop.advices.intruduction;
import java.util.Date;
public interface IAuditable {
void setLastModifiedDate(Date date);
Date getLastModifiedDate();
}
package com.osiris.springaop.advices.intruduction;
import java.util.Date;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.IntroductionInterceptor;
public class AuditableMixin implements IAuditable, IntroductionInterceptor {
private Date lastModifiedDate;
public Object invoke(MethodInvocation mi) throws Throwable {
if (implementsInterface(mi.getMethod().getDeclaringClass())) {
return mi.getMethod().invoke(this, mi.getArguments()); // 委派給 IAuditable 方法
}
return mi.proceed(); // 委派給目標業務方法
}
public Date getLastModifiedDate() { return lastModifiedDate; }
public void setLastModifiedDate(Date date) { lastModifiedDate = date; }
public boolean implementsInterface(Class cls) {
return IAuditable.class.isAssignableFrom(cls); // 注意:原文邏輯有誤,應為 cls.isAssignableFrom(IAuditable.class) 或 IAuditable.class.isAssignableFrom(cls)
}
}
public interface BookService {
String OrderBook(String userName, String bookName);
String OrderComputerMagazine(String userName, String bookName);
}
public class BookServiceImpl implements BookService {
public String OrderBook(String name, String bookName) {
return "訂購" + bookName + "成功";
}
public String OrderComputerMagazine(String userName, String bookName) {
return "訂購" + bookName + "成功";
}
}
<bean id="bookService" class="com.osiris.springaop.BookServiceImpl"/>
<bean id="auditableMixin" class="com.osiris.springaop.advices.intruduction.AuditableMixin"/>
<aop:config>
<aop:advisor advice-ref="auditableMixin"
pointcut="bean(bookService)"/> <!-- Introduction 需特殊配置,通常用 ProxyFactoryBean -->
</aop:config>
BookService bookService = (BookService) context.getBean("bookService");
bookService.OrderBook("user", "Professional C#");
// 強制轉型調用混入方法
IAuditable auditable = (IAuditable) bookService;
auditable.setLastModifiedDate(new Date());
System.out.println("訂購時間為 " + auditable.getLastModifiedDate());
輸出:
訂購Professional C#成功 訂購時間為Mon Nov 07 11:35:20 CST 2005