Spring AOP 核心在於代理模式:為目標物件建立代理,在方法調用前後織入橫切關注點(日誌、事務、安全等)。本文以 ProxyFactoryBean 為例,示範如何透過 XML 配置實現 MethodBeforeAdvice(方法前置增強),並解析關鍵組件與運行流程。
| 組件 | 角色 | 關鍵介面/類別 |
|---|---|---|
| 目標物件 | 被代理的業務邏輯實現 | 實現業務介面的 POJO |
| 增強 | 橫切邏輯(前置、後置、環繞、拋異常、引介) | MethodBeforeAdvice, AfterReturningAdvice, MethodInterceptor... |
| 切點 | 定義「在哪些方法」織入增強 | Pointcut, PointcutAdvisor |
| 代理工廠 | 生成代理物件的核心 | ProxyFactoryBean (XML), ProxyFactory (程式化) |
package com.example.aop;
public interface HelloService {
void sayHello(String name);
}
public class HelloServiceImpl implements HelloService {
@Override
public void sayHello(String name) {
System.out.println("Hello, " + name);
}
}
package com.example.aop;
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
public class LoggingBeforeAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("[LOG] 方法調用: " + method.getName() + ", 參數: " + Arrays.toString(args));
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://www.springframework.org/schema/beans"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 目標 Bean -->
<bean id="helloService" class="com.example.aop.HelloServiceImpl" />
<!-- 增強 Bean -->
<bean id="loggingBeforeAdvice" class="com.example.aop.LoggingBeforeAdvice" />
<!-- 代理工廠 Bean -->
<bean id="helloServiceProxy"
class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 代理實現的介面 (JDK 動態代理) -->
<property name="proxyInterfaces">
<value>com.example.aop.HelloService</value>
</property>
<!-- 目標物件 -->
<property name="target" ref="helloService" />
<!-- 攔截器/增強鏈 -->
<property name="interceptorNames">
<list>
<value>loggingBeforeAdvice</value>
</list>
</property>
</bean>
</beans>
package com.example.aop;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AopDemo {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("advice-config.xml");
HelloService proxy = (HelloService) ctx.getBean("helloServiceProxy");
proxy.sayHello("Spring AOP");
}
}
[LOG] 方法調用: sayHello, 參數: [Spring AOP]
Hello, Spring AOP
| 屬性 | 說明 | 備註 |
|---|---|---|
proxyInterfaces | 代理實現的介面清單 | 若目標類實現介面,使用 JDK 動態代理;否則需 proxyTargetClass=true 強制 CGLIB |
target | 目標物件引用 | 可引用同一容器內的 Bean |
interceptorNames | 攔截器/增強 Bean 名稱清單 | 順序決定執行順序;支援 Advisor、Advice、MethodInterceptor |
proxyTargetClass | 強制使用 CGLIB 代理類而非介面 | 預設 false;目標類無介面或需代理具體方法時設 true |
optimize | 優化代理生成 (CGLIB) | 預設 false |
frozen | 凍結代理配置,禁止運行時修改 | 預設 false |
現代 Spring 專案推薦使用 @Aspect、@Before、@Pointcut 註解配合 @EnableAspectJAutoProxy,優勢在於:
等價註解寫法:
@Aspect
@Component
public class LoggingAspect {
@Before("execution(* com.example.aop.HelloService.*(..))")
public void logBefore(JoinPoint jp) {
System.out.println("[LOG] " + jp.getSignature() + " args=" + Arrays.toString(jp.getArgs()));
}
}
ProxyFactoryBean 是 Spring 早期 AOP 配置的核心,透過 proxyInterfaces、target、interceptorNames 三大屬性組裝代理。理解其運作原理,有助於深入掌握 Spring AOP 底層機制。現代開發請優先採用 @AspectJ 註解風格,享受更優雅、類型安全的切面程式設計體驗。