若直接在業務類別(如 Math)每個方法中硬編碼日誌、計時等橫切邏輯,會違反:
java.lang.reflect.Proxy)InvocationHandler 處理所有介面方法。public class DynamicProxy implements InvocationHandler {
private Object target;
public Object getProxy(Object target) {
this.target = target;
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this);
}
public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
long start = System.currentTimeMillis();
Object result = m.invoke(target, args);
System.out.println("耗時:" + (System.currentTimeMillis() - start));
return result;
}
}
final 類別/方法。public class CglibProxy implements MethodInterceptor {
public Object getProxy(Object target) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
public Object intercept(Object obj, Method m, Object[] args, MethodProxy proxy) throws Throwable {
long start = System.currentTimeMillis();
Object result = proxy.invokeSuper(obj, args); // 注意這裡用 invokeSuper
System.out.println("耗時:" + (System.currentTimeMillis() - start));
return result;
}
}
ProxyFactory)ProxyFactory factory = new ProxyFactory();
factory.setTarget(new Math());
factory.addAdvice(new TimeSpanAdvice()); // 環繞通知
IMath proxy = (IMath) factory.getProxy();
ProxyFactoryBean)<bean id="target" class="com.demo.Math"/>
<bean id="advice" class="com.demo.TimeSpanAdvice"/>
<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean"
p:target-ref="target" p:interceptorNames="advice"
p:proxyTargetClass="true"/> <!-- true 強制 CGLIB -->
<aop:config proxy-target-class="true">
<aop:aspect ref="advice">
<aop:pointcut id="pc" expression="execution(* com.demo.Math.*(..))"/>
<aop:before method="before" pointcut-ref="pc"/>
<aop:after method="after" pointcut-ref="pc"/>
<aop:around method="around" pointcut-ref="pc"/>
<aop:after-returning method="afterReturn" pointcut-ref="pc" returning="ret"/>
<aop:after-throwing method="afterThrow" pointcut-ref="pc" throwing="ex"/>
</aop:aspect>
</aop:config>
| 通知 | 介面/註解 | 典型用途 |
|---|---|---|
| 前置 | MethodBeforeAdvice / @Before | 權限檢查、日誌 |
| 後置 | AfterReturningAdvice / @AfterReturning | 結果封裝、資源清理 |
| 環繞 | MethodInterceptor / @Around | 事務管理、計時、快取 |
| 拋出 | ThrowsAdvice / @AfterThrowing | 異常統一處理 |
| 返回 | - / @AfterReturning(returning=..) | 後處理回傳值 |
proxy-target-class="true")。掌握代理演進脈絡,能在面對效能、相容性、維護性取捨時做出正確架構決策。