回顧一下JDK動態(tài)代理和cglib動態(tài)代理,轉(zhuǎn)自我的BLOG:
閑言少敘,先來說一下什么是代理模式,我們?nèi)ヒ粋€新的地方總是要先找地方住,但是我們?nèi)松夭皇斓恼莆盏馁Y源不多,這時候一般會找中介,中介對房源很熟悉,很快就能為你找到合適的房子,這時候,中介就是一個代理,你就相當(dāng)于是一個委托方。
下面是設(shè)計模式中的代理:
代理模式
代理模式是常用的java設(shè)計模式,他的特征是代理類與委托類有同樣的接口,代理類主要負責(zé)為委托類預(yù)處理消息、過濾消息、把消息轉(zhuǎn)發(fā)給委托類,以及事后處理消息等。代理類與委托類之間通常會存在關(guān)聯(lián)關(guān)系,一個代理類的對象與一個委托類的對象關(guān)聯(lián),代理類的對象本身并不真正實現(xiàn)服務(wù),而是通過調(diào)用委托類的對象的相關(guān)方法,來提供特定的服務(wù)。
按照代理的創(chuàng)建時期,代理類可以分為兩種:
靜態(tài)代理
由程序員創(chuàng)建或特定工具自動生成源代碼,再對其編譯。在程序運行前,代理類的.class文件就已經(jīng)存在了。
動態(tài)代理
在程序運行時,運用反射機制動態(tài)創(chuàng)建而成。 動態(tài)代理類的字節(jié)碼在程序運行時由Java反射機制動態(tài)生成,無需程序員手工編寫它的源代碼。動態(tài)代理類不僅簡化了編程工作,而且提高了軟件系統(tǒng)的可擴展性,因為Java 反射機制可以生成任意類型的動態(tài)代理類。java.lang.reflect 包中的Proxy類和InvocationHandler 接口提供了生成動態(tài)代理類的能力。
JDK動態(tài)代理
先來看下JDK源碼中InvocationHandler中invoke()方法
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
JDK源碼中Proxy類的代碼:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
if (h == null) {
throw new NullPointerException();
}
/*
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass(loader, interfaces);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
Constructor cons = cl.getConstructor(constructorParams);
return cons.newInstance(new Object[] { h });
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString());
} catch (IllegalAccessException e) {
throw new InternalError(e.toString());
} catch (InstantiationException e) {
throw new InternalError(e.toString());
} catch (InvocationTargetException e) {
throw new InternalError(e.toString());
}
}
參數(shù)說明:
ClassLoader loader:類加載器
Class<?>[] interfaces:得到全部的接口
InvocationHandler h:得到InvocationHandler接口的子類實例
PS:類加載器
在Proxy類中的newProxyInstance()方法中需要一個ClassLoader類的實例,ClassLoader實際上對應(yīng)的是類加載器,
在Java中主要有一下三種類加載器:
Booststrap ClassLoader:此加載器采用C++編寫,一般開發(fā)中是看不到的;
Extendsion ClassLoader:用來進行擴展類的加載,一般對應(yīng)的是jre\lib\ext目錄中的類;
AppClassLoader:(默認)加載classpath指定的類,是最常使用的是一種加載器。
JDK動態(tài)代理實現(xiàn)步驟
實現(xiàn)InvocationHandler接口
獲得代理對象
public Object getInstance(Object target){
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
回調(diào)函數(shù)
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
System.out.println("jdk 動態(tài)代理 begin...");
result = method.invoke(target,args);
System.out.println("jdk 動態(tài)代理 end...");
return result;
}
JDK動態(tài)代理缺點:
只能對實現(xiàn)了接口的類進行,沒有實現(xiàn)接口的類不能使用JDK動態(tài)代理。
cglib動態(tài)代理
JDK的動態(tài)代理機制只能代理實現(xiàn)了接口的類,而不能實現(xiàn)接口的類就不能實現(xiàn)JDK的動態(tài)代理,cglib是針對類來實現(xiàn)代理的,他的原理是對指定的目標類生成一個子類,并覆蓋其中方法實現(xiàn)增強,但因為采用的是繼承,所以不能對final修飾的類進行代理。
cglib實現(xiàn)動態(tài)代理的方法和JDK動態(tài)代理類似
實現(xiàn)MethodInterceptor接口
獲得代理對象
public Object getInstance(Object target){
this.target = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass());
//設(shè)置回調(diào)方法
enhancer.setCallback(this);
//創(chuàng)建代理對象
return enhancer.create();
}
設(shè)置回調(diào)方法
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("UserFacadeProxy.intercept begin");
methodProxy.invokeSuper(o,objects);
System.out.println("UserFacadeProxy.intercept end");
return null;
}
Spring AOP原理
java動態(tài)代理是利用反射機制生成一個實現(xiàn)代理接口的匿名類,在調(diào)用具體方法前調(diào)用InvokeHandler來處理。而cglib動態(tài)代理是利用asm開源包,對代理對象類的class文件加載進來,通過修改其字節(jié)碼生成子類來處理。
SpringAOP動態(tài)代理策略是:
1、如果目標對象實現(xiàn)了接口,默認情況下會采用JDK的動態(tài)代理實現(xiàn)AOP
2、如果目標對象實現(xiàn)了接口,可以強制使用CGLIB實現(xiàn)AOP
3、如果目標對象沒有實現(xiàn)了接口,必須采用CGLIB庫,spring會自動在JDK動態(tài)代理和CGLIB之間轉(zhuǎn)換