| 方式 | 核心機制 | 配置形式 | 適用場景 | 學習難度 |
|---|---|---|---|---|
| 1. 經典 ProxyFactoryBean | 手工裝配代理工廠、顧問、通知 | XML Bean 定義 | 教學理解原理、極簡項目、舊版本維護 | ★★★☆☆ |
| 2. 自動代理創建器 | BeanPostProcessor 掃描 Advisor 自動代理 | XML 宣告 AutoProxyCreator | Spring 內部機制、批量代理固定模式 | ★★☆☆☆ |
| 3. @AspectJ 註解 | 註解驅動,AspectJ 切點表達式 | Java 註解 + @EnableAspectJAutoProxy | 現代主流標準、功能最全、重構友好 | ★★☆☆☆ |
| 4. Schema XML (aop:config) | 命名空間配置,純 POJO 切面 | XML aop:config/aop:aspect | 無法修改源碼第三方類、配置外部化需求 | ★★☆☆☆ |
public interface Advice {
void firstMethod();
String secondMethod();
}
public class AdviceImpl implements Advice {
public void firstMethod() { System.out.println("==firstMethod=="); }
public String secondMethod() { System.out.println("==secondMethod=="); return "abc"; }
}
// 前置通知
public class MethodBeforeAdvice implements org.springframework.aop.MethodBeforeAdvice {
public void before(Method method, Object[] args, Object target) {
System.out.println("==Before==");
}
}
// 後置通知
public class AfterReturningAdvice implements org.springframework.aop.AfterReturningAdvice {
public void afterReturning(Object ret, Method method, Object[] args, Object target) {
System.out.println("==After==");
}
}
// 環繞通知 (最強大,實現 MethodInterceptor)
public class MyInterceptor implements org.aopalliance.intercept.MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("==Around Before==");
Object result = invocation.proceed(); // 執行目標方法
System.out.println("==Around After==");
return result;
}
}
// 異常通知
public class MyException implements org.springframework.aop.ThrowsAdvice {
public void afterThrowing(Exception ex) {
System.out.println("異常通知: " + ex.getMessage());
}
}
<beans>
<bean id="adviceImpl" class="com.example.AdviceImpl"/>
<bean id="beforeAdvice" class="com.example.MethodBeforeAdvice"/>
<bean id="afterAdvice" class="com.example.AfterReturningAdvice"/>
<bean id="aroundAdvice" class="com.example.MyInterceptor"/>
<bean id="throwAdvice" class="com.example.MyException"/>
<!-- 顧問:方法名匹配 -->
<bean id="nameMatchAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
<property name="advice" ref="beforeAdvice"/>
<property name="mappedNames" value="firstMethod,secondMethod"/>
</bean>
<!-- 顧問:正則匹配 -->
<bean id="regexAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice" ref="aroundAdvice"/>
<property name="pattern" value=".*Method.*"/>
</bean>
<!-- 自動代理:BeanNameAutoProxyCreator -->
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames" value="adviceImpl"/>
<property name="interceptorNames" value="nameMatchAdvisor,regexAdvisor"/>
</bean>
</beans>
// DAO
public interface UDao { void save(User user); }
public class UserDaoImpl implements UDao { public void save(User u) { System.out.println("保存成功"); } }
// Biz
public interface UBiz { void save2(User user); }
public class UserBiz implements UBiz {
private UDao udao;
public void save2(User u) { udao.save(u); }
// setter/getter
}
// Service (通知)
public class LoggerBefore implements MethodBeforeAdvice {
public void before(Method m, Object[] args, Object t) { System.out.println("save前置"); }
}
public class LoggerAfter implements AfterReturningAdvice {
public void afterReturning(Object r, Method m, Object[] args, Object t) { System.out.println("save後置"); }
}
// XML 配置 (aop:config 簡化版)
<aop:config>
<aop:pointcut expression="execution(public void save2(..))" id="pc"/>
<aop:advisor advice-ref="before" pointcut-ref="pc"/>
<aop:advisor advice-ref="after" pointcut-ref="pc"/>
</aop:config>
@Aspect
@Component
public class Aspect {
@Before("execution(public * *(..))")
public void asBefore() { System.out.println("前置增強"); }
@AfterReturning("execution(public * *(..))")
public void asAfterReturning() { System.out.println("後置增強"); }
@Around("execution(public * *(..))")
public void asAround(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("環繞前");
pjp.proceed();
System.out.println("環繞後");
}
@AfterThrowing("execution(public * *(..))")
public void asThrowing() { System.out.println("異常增強"); }
@After("execution(public * *(..))")
public void asAfter() { System.out.println("最終增強"); }
}
<beans>
<bean id="aspectImpl" class="com.example.AspectjImpl"/>
<bean id="aspect" class="com.example.Aspect"/>
<aop:aspectj-autoproxy /> <!-- 關鍵:啟用註解掃描 -->
</beans>
// 切面類:移除所有註解,保留普通方法
public class Aspect {
public void asBefore() { System.out.println("前置增強"); }
public void asAfterReturning() { System.out.println("後置增強"); }
public void asAround(ProceedingJoinPoint pjp) throws Throwable { /*...*/ }
public void asThrowing() { System.out.println("異常增強"); }
public void asAfter() { System.out.println("最終增強"); }
}
<aop:config>
<aop:pointcut expression="execution(public * *(..))" id="pc"/>
<aop:aspect ref="aspect">
<aop:before method="asBefore" pointcut-ref="pc"/>
<aop:after-returning method="asAfterReturning" pointcut-ref="pc"/>
<!-- 環繞需傳 ProceedingJoinPoint 參數 -->
<aop:around method="asAround" pointcut-ref="pc"/>
<aop:after-throwing method="asThrowing" pointcut-ref="pc"/>
<aop:after method="asAfter" pointcut-ref="pc"/>
</aop:aspect>
</aop:config>
// 正常流程 @Before → @Around (proceed前) → 目標方法 → @Around (proceed後) → @AfterReturning → @After // 異常流程 @Before → @Around (proceed前) → 目標方法拋異常 → @Around (catch塊/拋出) → @AfterThrowing → @After // 注意:@AfterReturning 不執行,@After 最終增強始終執行 (類似 finally)
proxy-target-class=true 強制 CGLIB無論哪種方式,本質都是 BeanPostProcessor + 動態代理 (JDK/CGLIB) 在 Bean 初始化後攔截替換。掌握切點表達式、通知類型、執行順序、代理機制四大核心,即可駕馭 Spring AOP 解決所有橫切關注點問題。