動態(tài)代理介紹
應(yīng)用場景
假設(shè)現(xiàn)在已經(jīng)存在一個children接口墓拜,其中一個方法是eat()你家有個小孩myChild類循帐,你希望他講衛(wèi)生懂禮貌筝家,以后成為新世紀的四好少年旋讹,所以要讓他在吃飯前洗手瑰钮,為了能夠有時間打王者榮耀需要讓他在吃飯后就睡覺辙售。那這個時候的第一想法可能在children類中eat()方法的前后增加washHands和sleep的操作,或者增加方法washHand()和sleep()飞涂,然后在eat()方法中引用旦部。這兩種方法都可以解決這個問題,但是開閉原則告訴我們-對修改關(guān)閉╮(╯▽╰)╭较店。這樣就堵死了這兩種簡單的方式士八。在你一籌莫展的時候,動態(tài)代理就站在了你的面前梁呈,拯救你于水火之中婚度。
原理
我們在解決問題的時候往往想的就是簡單直接的解決當前面對的問題,但是現(xiàn)實的情況往往不能如愿官卡,既然不能夠直接修改myChild類蝗茁,那么就只能在你家小孩吃的之前增加一道洗手的程序,然后還是讓你的小孩吃飯寻咒,吃飯之后再增加一道睡覺的程序哮翘。這個時候就相當于在吃飯這個操作的前后增加了不同的操作,但是對于吃飯本身來說并沒有任何影響毛秘。
分類
動態(tài)代理分為兩種:JDK提供的動態(tài)代理和第三方的CGLib
JDK動態(tài)代理示例
JDK動態(tài)代理簡介
JDK動態(tài)代理顧名思義是由JDK提供的一種動態(tài)代理方式饭寺,簡介結(jié)束。
類圖
具體代碼
撒話不說直接上代碼
接口Children
public interface Children {
public void eat();
}
實現(xiàn)類MyChild
public class MyChild implements Children {
@Override
public void eat() {
System.out.println("eat Something");
}
}
處理類DynamicChildren
public class DynamicChildren implements InvocationHandler {
private Object children;
public DynamicChildren(Object object){
this.children = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Wash Hands!");
// 調(diào)用實際實例的eat方法
method.invoke(children, args);
System.out.println("Sleep!");
return null;
}
}
客戶端Client
public class Client {
public static void main(String[] args) {
MyChild myChild = new MyChild();
InvocationHandler handler = new DynamicChildren(myChild);
Class<?> classType = handler.getClass();
/*classType.getClassLoader():獲得DynamicChildren類加載器
myChild.getClass().getInterfaces():獲得MyChild實現(xiàn)的所有接口類叫挟,當前的MyChild只實現(xiàn)了Children一類接口艰匙,所以這獲得的只有這個
handler:DynamicChildren
children即為生成的動態(tài)代理類*/
Children children = (Children) Proxy.newProxyInstance(classType.getClassLoader(), myChild.getClass().getInterfaces(), handler);
// 這實際上是調(diào)用的DynamicChildren中的invoke方法
children.eat();
}
}
小結(jié)
以上就是我理解的JDK提供的動態(tài)代理相關(guān)的東西,完美是相對的抹恳,JDK動態(tài)代理也是一樣有優(yōu)缺點的员凝。
優(yōu)點
- 不依賴第三方j(luò)ar包, 使用方便
- 隨著JDK的升級,JDK動態(tài)代理的性能在穩(wěn)步提升
缺點
- 只適用于實現(xiàn)了接口類
- 執(zhí)行速度較慢
看了以上的內(nèi)容奋献,你的心中一定會冒出一個疑問健霹,如果沒有實現(xiàn)接口的類想要動態(tài)代理硕舆,是不是需要另外一種代理方式了呢?你猜的沒錯骤公!這個時候就到了另一種動態(tài)代理-CGLibの出番だ!
CGLib簡介
CGLIB(Code Generation Library)是一個開源項目扬跋!
是一個強大的阶捆,高性能,高質(zhì)量的Code生成類庫钦听,它可以在運行期擴展Java類與實現(xiàn)Java接口洒试。Hibernate> 支持它來實現(xiàn)PO(Persistent Object 持久化對象)字節(jié)碼的動態(tài)生成。
以上摘自百度百科
CGLib的原理是通過字節(jié)碼技術(shù)為一個類創(chuàng)建子類朴上,并在子類中采用方法攔截的技術(shù)攔截當前類的所有父類方法的調(diào)用垒棋,順勢織入橫切邏輯。
使用的前置條件
需要在項目中依賴cglib相關(guān)的jar包和asm jar包痪宰。我使用的是cglib-2.2.jar叼架、cglib-nodep-2.2.jar和asm-3.2.jar。
類圖
具體代碼
撒話不說直接上代碼
需要代理的類MyChild
public class MyChild {
public void eat() {
System.out.println("eat something");
}
}
處理類CglibProxy
public class CglibProxy implements MethodInterceptor {
private Enhancer enhancer = new Enhancer();
public Object getProxy(Class clazz) {
// 設(shè)置需要創(chuàng)建動態(tài)代理類的類
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
// 通過字節(jié)碼動態(tài)創(chuàng)建實例
return enhancer.create();
}
/**
* 實現(xiàn) MethodInterceptor接口方法
*/
@Override
public Object intercept(Object obj, Method method, Object[] arg, MethodProxy proxy) throws Throwable {
System.out.println("Wash Hands!");
// 通過代理類調(diào)用父類中的方法
Object result = proxy.invokeSuper(obj, arg);
System.out.println("Sleep!");
return result;
}
}
客戶端類DoCGLib
public class DoCGLib {
public static void main(String[] args) {
CglibProxy proxy = new CglibProxy();
// 通過生成子類的方式來生成代理類
MyChild myChild = (MyChild) proxy.getProxy(MyChild.class);
myChild.eat();
}
}
小結(jié)
以上就是我理解的CGLib動態(tài)代理相關(guān)的東西衣撬,JDK動態(tài)代理有優(yōu)缺點乖订,那么CGLib動態(tài)代理也不能少。
優(yōu)點
- 可以代理沒有實現(xiàn)接口的對象
- 由于是動態(tài)生成字節(jié)碼實現(xiàn)代理具练,因此代理對象的執(zhí)行速度較快, 約為JDK動態(tài)代理的1.5 ~ 2倍
缺點
- 不能代理final類
- 動態(tài)生成字節(jié)碼雖然執(zhí)行較快乍构,但是生成速度很慢,根據(jù)網(wǎng)上一些人的測試結(jié)果扛点,CGLib創(chuàng)建代理對象的速度要比JDK慢10 ~ 15倍哥遮。
JDK動態(tài)代理和CGLib適用場景
- 由于JDK動態(tài)代理在創(chuàng)建代理對象上比CGLib快,所以如果你的程序需要頻繁陵究、反復地創(chuàng)建代理對象眠饮,請將JDK動態(tài)代理定位為備胎一號。
- 由于CGLib的執(zhí)行速度比JDK快铜邮,所以如果不需要頻繁創(chuàng)建代理對象的應(yīng)用君仆,如spring中默認的單例bean,只需要在容器啟動時生成一次代理對象這種情況牲距,請優(yōu)先考慮轉(zhuǎn)正CGLib返咱。
以上です!