| 術語 | 定義 | 本文對應 |
|---|---|---|
| Join Point | 程式執行點 (方法呼叫、異常拋出...) | 方法執行 |
| Pointcut | 切點運算式,選擇 Join Point 集合 | .*print.* |
| Advice | 切面邏輯 (Before/After/Around/Throws) | PrintBeforeAdvice |
| Aspect | Pointcut + Advice 模組化 | Advisor 封裝 |
| Target | 被代理對象 | FooImpl |
| Proxy | 代理對象,攔截請求並織入 Advice | ProxyFactory.getProxy() |
| Weaving | 將 Advice 植入 Target 的過程 | 運行期動態代理 |
// 目標介面與實現
public interface FooInterface { void printFoo(); void dummyFoo(); }
public class FooImpl implements FooInterface {
public void printFoo() { System.out.println("In FooImpl.printFoo"); }
public void dummyFoo() { System.out.println("In FooImpl.dummyFoo"); }
}
// Before Advice
public class PrintBeforeAdvice implements MethodBeforeAdvice {
public void before(Method m, Object[] args, Object target) {
System.out.println("In PrintBeforeAdvice");
}
}
// 組裝代理
public class AopTestMain {
public static void main(String[] args) {
FooImpl target = new FooImpl();
PrintBeforeAdvice advice = new PrintBeforeAdvice();
ProxyFactory factory = new ProxyFactory(target);
factory.addAdvice(advice); // 預設為 Before
FooInterface proxy = (FooInterface) factory.getProxy();
proxy.printFoo();
proxy.dummyFoo();
}
}
// 輸出:
// In PrintBeforeAdvice
// In FooImpl.printFoo
// In PrintBeforeAdvice
// In FooImpl.dummyFoo
缺點:代碼耦合、難維護、非聲明式。
<beans xmlns="https://www.springframework.org/schema/beans"
xmlns:aop="https://www.springframework.org/schema/aop">
<!-- 目標 Bean -->
<bean id="fooTarget" class="com.example.FooImpl"/>
<!-- Advice Bean -->
<bean id="printBeforeAdvice" class="com.example.PrintBeforeAdvice"/>
<!-- ProxyFactoryBean (Spring 1.x/2.x 風格) -->
<bean id="foo" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="fooTarget"/>
<property name="interceptorNames">
<list><value>printBeforeAdvice</value></list>
</property>
</bean>
<!-- 或 Schema-based (Spring 2.0+ 推薦) -->
<aop:config>
<aop:advisor advice-ref="printBeforeAdvice" pointcut="execution(* printFoo())"/>
</aop:config>
</beans>
<aop:config>
<aop:pointcut id="printMethods" expression="execution(* *print*())"/>
<aop:advisor advice-ref="printBeforeAdvice" pointcut-ref="printMethods"/>
</aop:config>
結果:僅 printFoo() 觸發 Advice,dummyFoo() 不觸發。
@Aspect
@Component
public class LoggingAspect {
@Before("execution(* com.example..*print*())")
public void logBefore(JoinPoint jp) {
System.out.println("In Aspect: " + jp.getSignature());
}
}
// 配置啟用:@EnableAspectJAutoProxy
<!-- DataSource + Hibernate SessionFactory -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
</props>
</property>
</bean>
<!-- 事務管理器 -->
<bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<!-- TX Advice 定義 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="get*" read-only="true"/>
<tx:method name="find*" read-only="true"/>
<tx:method name="load*" read-only="true"/>
<tx:method name="*"/> <!-- 默認 REQUIRED -->
</tx:attributes>
</tx:advice>
<!-- AOP 織入事務 -->
<aop:config>
<aop:pointcut id="serviceMethods" expression="execution(* com.example.service.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethods"/>
</aop:config>
| 特性 | JDK Dynamic Proxy | CGLIB |
|---|---|---|
| 前提 | 目標實現介面 | 無需介面 (繼承類) |
| 原理 | 實現介面、委派調用 | 繼承目標類、覆寫方法 |
| final 方法 | 不可代理 | 不可代理 |
| 啟用 | 預設 (有介面) | proxy-target-class="true" 或 @EnableAspectJAutoProxy(proxyTargetClass=true) |
Spring AOP 以 運行期動態代理 實現橫切關注點分離。演進路徑:ProxyFactory (教學) → ProxyFactoryBean (XML) → aop:config (Schema) → @AspectJ (註解)。核心價值在於 聲明式事務、安全、日誌、快取、監控等基礎設施邏輯與業務代碼零耦合。現代 Spring Boot 專案:@EnableTransactionManagement + @Transactional 即可獲得完整事務 AOP,底層仍是此機制。