1.動態(tài)代理
1.1什么是動態(tài)代理?
動態(tài)代理(理解) 基于反射機制
舉個例子,生活中一般在打官司的時候都會請代理律師,為什么要請律師呢?是因為開庭的時候大部人對于打官司沒有經(jīng)驗,只會說出自己案件的陳述,并不會根據(jù)法律等爭取自己權益的最大化,此時就可以請律師幫助自己不僅完成對案件的陳述,還能爭取權益最大化。那么Java中也是一樣,如果要對功能進行增強就可以使用動態(tài)代理。
我們知道Spring是通過JDK或者CGLib實現(xiàn)動態(tài)代理的,今天我們討論一下JDK實現(xiàn)動態(tài)代理的原理。
1.2動態(tài)代理的實現(xiàn)方式有幾種?
- JDK動態(tài)代理
- CGLB動態(tài)代理
1.3動態(tài)代理的概念和優(yōu)點
相比于靜態(tài)代理(在靜態(tài)代理中,對于類的每一個接口,我們都要單獨寫一個代理類),動態(tài)代理在創(chuàng)建代理對象上更加的靈活。
有需求,有問題,才會有解決方法和改進創(chuàng)新。那么產(chǎn)生動態(tài)代理的問題根源或者需求是什么呢?在靜態(tài)代理中,由于每個接口都需要我們單獨的寫一個代理類,比較麻煩,因此我們就想,我們能不能寫一個類,我們只需要把委托對象(目標對象),還有全部接口(共同行為,其實委托對象中就已經(jīng)包含接口了)作為參數(shù)傳給這個類的方法,然后這個方法就可以給我們返回一個我們想要的代理對象呢,并且這個代理對象可以給我們代理全部的共同行為,像是租房,結婚等?
- 動態(tài)代理類的字節(jié)碼在程序運行時由JAVA反射機制動態(tài)產(chǎn)生。會根據(jù)需要,通過反射機制,在程序運行期動態(tài)的為目標對象(委托對象)創(chuàng)建代理對象,無需程序員手動編寫代理對象所屬類的代碼。
- 動態(tài)代理不僅簡化了編程工作,而且提高了軟件系統(tǒng)的可擴展性,因為反射機制的使用使得可以生成任意類型的代理對象。
- 動態(tài)代理的實現(xiàn)方式有兩種,分別是:JDK動態(tài)代理和CGLIB動態(tài)代理。
- 動態(tài)代理的目標對象是不固定的(也就是說把任何一個目標對象或者說是委托對象作為參數(shù)傳遞給生成代理對象的對象的方法,都會給我們返回一個我們想要的代理對象);使用動態(tài)代理以后,會在應用程序執(zhí)行的時候,動態(tài)的創(chuàng)建目標對象代理對象依然會增強目標對象的行為;
2.JDK動態(tài)代理的概念和特點
先說一個需要注意的點:JDK動態(tài)代理的目標對象必須要有接口實現(xiàn),也就是說:委托類必須要繼承接口。
在JDK中,有一個Proxy類(名詞,代理人)。Proxy類是專門完成代理的操作類,可以通過此類為一個或多個接口動態(tài)的生成實現(xiàn)類(對這個類的其他方法我了解的也不是很多,我們可以看JDK的在線API文檔,百度搜一下好像挺多的)。這個類提供的有一個靜態(tài)方法:newProxyInstance()方法。這個方法的目的就是給我們的目標對象(委托對象)返回一個代理對象。
newProxyInstance()方法需要有三個參數(shù):
- 類加載器(ClassLoader對象)
- 接口集合(一個Interface對象的數(shù)組,就是需要代理對象代理那些共同行為,也是委托對象繼承的共同行為接口)
- 一個InvocationHandler接口對象(當然可以是它的一個實現(xiàn)類對象)。這個接口中有一個invoke()方法。invoke()方法起到的作用很大,當代理對象調用共同行為方法的時候,invoke()方法就會被自動調用執(zhí)行。
下面粘貼一張圖片:
2.1動態(tài)代理的介紹
- ?動態(tài)代理是指代理類對象在程序運行時由JVM根據(jù)反射機制動態(tài)生成的。動態(tài)代理不需要定義代理類的,java源文件。
- 動態(tài)代理其實就是jdk運行期間,動態(tài)創(chuàng)建class字節(jié)碼并加載到JVM。
- 動態(tài)代理的實現(xiàn)方式常用的有兩種:使用JDK代理,與通過CGLlB動態(tài)代理。
2.2動態(tài)代理的實現(xiàn)
- jdk動態(tài)代理(理解):使用java反射包中的類和接口實現(xiàn)動態(tài)代理的功能,反射包java.lang.reflect,里面有三個類:InvocationHandler,Method,Proxy
- cglib動態(tài)代理(了解): cglib是第三方的工具庫,創(chuàng)建代理對象
- cglib的原理是繼承,cglib通過繼承目標類,創(chuàng)建它的子類,在子類中
重寫父類中同名的方法,實現(xiàn)功能的修改。- 因為cglib是繼承,重寫方法,所以要求目標類不能是fina1的,方法也不能是final的。cglib的要求目標類比較寬松,只要能繼承就可以了。cglib在很多的框架中使用,
比如mybatis,spring框架中都有使用
2.3CGLB動態(tài)代理
CGLB動態(tài)代理即Code Generation Library,是一個開源的第三方工具庫,其原理是繼承,去生成目標類的子類對象,這樣對子類的功能進行增強。但是要求:目標類不能用final修飾,目標類中的方法也不能被final修飾。
2.4動態(tài)代理的效率
CGLB動態(tài)代理的效率要大于JDK動態(tài)代理的效率。
3.為什么使用JDK動態(tài)代理?
3.1代碼案例:
- 請看如下例子,一個音樂人的本質會唱歌、會跳舞、會rap等,但是觀眾進入演唱會所時首先要交門票,最基本的實現(xiàn)方法是直接對方法進行改造,添加對應的功能。
package com.zhao.service.impl; import com.zhao.service.Actor; public class CXK implements Actor { @Override public void sing(int money) { System.out.println("聽"+money); } @Override public String dance() { System.out.println("吹灰舞"); return "發(fā)放簽名"; } @Override public void rap() { System.out.println("練習時長兩年"); } }
-
這樣操作發(fā)現(xiàn)出現(xiàn)了大量重復的代碼,如果有十處、一百處需要同樣的處理那么代碼需要重復十次、一百次。當然我們可以把這些功能封裝成一個增強方法,然后在功能方法中進行調用,但是也出現(xiàn)了方法的十處、一百處的調用操作,一旦增強方法名字改變,就需要完成所有調用處代碼的修改?;蛘哂幸惶觳恍枰@些增強操作了,就再次需要在這十處、一百處刪除方法調用。所以這種操作不適用于大型的項目開發(fā)的需求,此時我們就必須使用Java的動態(tài)代理機制。
在Java開發(fā)中如果一個類中的方法在基本功能之外需要進行功能擴充或者功能增強,如:事務控制、權限判斷、日志記錄等等操作,此時可以使用動態(tài)代理機制。
Java的JDK中Proxy類可以實現(xiàn)基于接口的動態(tài)代理,實現(xiàn)步驟示例如下:
- 因為Proxy類必須基于接口進行動態(tài)代理,所以首先創(chuàng)建接口,定義接口的規(guī)范,即功能方法的定義。
package com.zhao.service; /** * 演員接口 * */ public interface Actor { //唱歌 void sing(int money); //跳舞 String dance(); //rap void rap(); }
- 定義實現(xiàn)接口的子類,實現(xiàn)接口定義的方法,此方法只需要把核心功能實現(xiàn)即可,其他增強的操作可以在代理類中實現(xiàn)。
package com.zhao.service.impl; import com.zhao.service.Actor; public class CXK implements Actor { @Override public void sing(int money) { System.out.println("聽"+money); } @Override public String dance() { System.out.println("吹灰舞"); return "發(fā)放簽名"; } @Override public void rap() { System.out.println("練習時長兩年"); } }
- 定義代理類,在代理類中對被代理對象進行方法增強。
package com.zhao.advice; import com.zhao.service.Actor; import com.zhao.service.impl.CXK; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class JJGS { public static void main(String[] args) { //1.創(chuàng)建被代理類的對象----具體的人物cxk Actor cxk=new CXK(); //2.創(chuàng)建代理對象--- 具體某人為上面cxk /** * ClassLoader loader:類的加載器---聯(lián)系方式 * Class<?>[] interfaces:類的接口類型---被代理人的類型 * InvocationHandleer h:處理---我要幫你干什么 */ Actor jjr=(Actor) Proxy.newProxyInstance(CXK.class.getClassLoader(), CXK.class.getInterfaces(), new InvocationHandler() { /** * * Object proxy:被代理對象的引用,系統(tǒng)會自動創(chuàng)建被代理對象的一個映射 * Method method:被代理對象的方法 * @param args *Object[] args:被代理對象方法的參數(shù) * 返回值是被代理對象執(zhí)行后的返回值 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //被代理對象方法的執(zhí)行,并獲得返回值 Object result=null; result=method.invoke(cxk,args); System.out.println("演出后的增強:結算費用并納稅"); return result; } }); //3.執(zhí)行功能 // jjr.sing(5000); String dance = jjr.dance(); System.out.println(dance); } }
? 測試結果:文章來源:http://www.zghlxwxcb.cn/news/detail-698510.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-698510.html
3.2基于子類的CGLib動態(tài)代理,可以使用Enhancer類完成直接對某個類進行動態(tài)代理。具體操作步驟如下:
- 創(chuàng)建被代理的類,并且定義功能方法,只需要完成核心功能即可。
package com.zhao.service.impl; public class Actor { public void sing(int money) { System.out.println("聽" + money); } public String dance(int money) { System.out.println("吹灰舞"+money); return "發(fā)放簽名"; } public void rap(int money) { System.out.println("練習時長兩年"+money); } }
- 定義代理類,使用Enhancer創(chuàng)建代理對象,對被代理對象進行方法增強。
package com.zhao.advice; import com.zhao.service.impl.Actor; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class JJGss { public static void main(String[] args) { //1.被代理的對象 Actor actor=new Actor(); //2.使用CGLIb的Enhancer類創(chuàng)建代理對象 Actor proxyActor = (Actor) Enhancer.create(Actor.class, new MethodInterceptor() { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { String methodName=method.getName(); String money=args[0]; Object result=null; if(methodName.equals("danceAct")){ System.out.println("對方法執(zhí)行前進性代理增強..."); result=method.invoke(actor,money); System.out.println("對方法執(zhí)行后進性代理增強..."); return result; } return result; } }); // proxyActor.dance(3000); } }
到了這里,關于Java——JDK動態(tài)代理的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!