Spring aop 是通過代理實(shí)現(xiàn)的,代理有靜態(tài)代理,jdk動態(tài)代理和cglib動態(tài)代理,代理就像我們生活中的房產(chǎn)中介,你不直接與房主,銀行接觸,而是通過中介與他們溝通聯(lián)系。
代理的結(jié)構(gòu)如圖所示:
RealSubject 和 Proxy都實(shí)現(xiàn)了相同的接口Subject,Proxy持有RealSubject的引用,但Client 調(diào)用 request方法時,Proxy將請求轉(zhuǎn)發(fā)給RealSubject,其中可以添加各種訪問控制。
但是靜態(tài)代理有一個弊端,需要為每一個目標(biāo)類創(chuàng)建一個代理類,如果需要代理的對象很多的話,就得編寫相應(yīng)的代理類,于是jdk動態(tài)代理出現(xiàn)了,它主要用了 java.lang.reflect.Proxy類和java.lang.reflect.InvocationHandler接口。
我們先實(shí)現(xiàn)java.lang.reflect.InvocationHandler,添加需要的橫切邏輯
public class AccessControl implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//調(diào)用前的處理
System.out.println("調(diào)用前驗(yàn)證");
Object obj = method.invoke(proxy, args);
//調(diào)用后的處理
System.out.println("調(diào)用后處理");
return obj;
}
}
然后通過Proxy為不同的類型生成相應(yīng)的代理對象。
Proxy.newProxyInstance(targertClass1.getClassLoader(), targertClass1.getInterfaces(),accessControl);
Proxy.newProxyInstance(targertClass2.getClassLoader(), targertClass2.getInterfaces(),accessControl);
不管targetClass有多少類型,都可以通過Proxy生成具有相同訪問控制accesControl的代理對象。
從上面示例可知jdk動態(tài)代理需要被代理類實(shí)現(xiàn)接口(Interface),對于沒有實(shí)現(xiàn)任何接口的目標(biāo)對象,我們就要另找方法了。默認(rèn)情況下,當(dāng)Spring發(fā)現(xiàn)目標(biāo)對象沒有實(shí)現(xiàn)任何接口時,會使用CGLIB,為目標(biāo)對象動態(tài)生成代理對象,其實(shí)質(zhì)就是對目標(biāo)對象進(jìn)行繼承,生成子類,子類覆蓋父類的方法,在其中加入額外的訪問控制,不過如果類中的方法聲明為final的話,就不能對它進(jìn)行擴(kuò)展。
Spring 創(chuàng)建代理的秘密在DefaultAopProxyFactory 類中可以找到:
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: "
\+ "Either an interface or a target is required for proxy creation.");
}
if (targetClass.isInterface()) {
return new JdkDynamicAopProxy(config);
}
return CglibProxyFactory.createCglibProxy(config);
} else {
return new JdkDynamicAopProxy(config);
}
}
如果isOptimize()返回true,或者proxyTargetClass屬性為true,或者目標(biāo)對象沒有接口實(shí)現(xiàn),就采用cglib動態(tài)代理,否則就用jdk動態(tài)代理。再看看JdkDynamicAopProxy中的getProxy
public Object getProxy(ClassLoader classLoader) {
if (logger.isDebugEnabled()) {
logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
}
Class[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
請看最后一行代碼,是否有似曾相識之感。
Spring aop 僅作用于方法,如果你想對構(gòu)造方法或字段作攔截處理,就要引入AspectJ,它支持在編譯期間織入橫切邏輯,提高運(yùn)行期間的性能,但在易用性和靈活性上不如Spring aop。值得注意的是,Spring中@AspectJ注解區(qū)別的切面也是基于Spring aop 的代理機(jī)制實(shí)現(xiàn)的,不要被這個名稱混淆了。