java中動態(tài)代理如何實現(xiàn)詳解
Java實現(xiàn)動態(tài)代理的兩種方式
- JDK動態(tài)代理:Java.lang.reflect 包中的Proxy類和InvocationHandler接口提供了生成動態(tài)代理類的能力。
- Cglib動態(tài)代理:Cglib (Code Generation Library )是一個第三方代碼生成類庫,運行時在內(nèi)存中動態(tài)生成一個子類對象從而實現(xiàn)對目標對象功能的擴展。
兩種動態(tài)代理的區(qū)別
JDK 的動態(tài)代理是基于接口的代理。
它要求被代理的類必須實現(xiàn)一個或多個接口。在運行時,JDK 動態(tài)代理會根據(jù)被代理類實現(xiàn)的接口生成一個代理對象,該代理對象實現(xiàn)了被代理類的接口,并將方法的調(diào)用轉發(fā)給真正的被代理類。JDK 動態(tài)代理的優(yōu)點是簡單易用,缺點是只能代理實現(xiàn)了接口的類。
CGLIB 是基于繼承的代理。
它可以代理沒有實現(xiàn)任何接口的類。在運行時,CGLIB 會動態(tài)生成一個被代理類的子類(Cglib包的底層是通過使用一個小而快的字節(jié)碼處理框架ASM,來轉換字節(jié)碼并生成新的類。不鼓勵直接使用ASM,因為它需要你對JVM內(nèi)部結構包括class文件的格式和指令集都很熟悉。),并重寫父類中的方法,從而實現(xiàn)代理功能。CGLIB 的優(yōu)點是可以代理沒有實現(xiàn)接口的類,缺點是生成的代理類需要繼承被代理類,并且無法代理 final 類型的方法。它可以在運行期擴展Java類與實現(xiàn)Java接口。它廣泛的被許多AOP的框架使用,例如Spring AOP和dynaop,為他們提供方法的interception(攔截)。
總結
使用JDK動態(tài)代理的對象必須實現(xiàn)一個或多個接口;而使用cglib代理的對象則無需實現(xiàn)接口,達到代理類無侵入。
JDK 的動態(tài)代理和 CGLIB 都有各自的優(yōu)點和缺點,具體使用哪種方式取決于具體的需求和場景。如果被代理的類已經(jīng)實現(xiàn)了接口,那么可以優(yōu)先考慮使用 JDK 的動態(tài)代理;如果被代理的類沒有實現(xiàn)接口,或者需要對類的所有方法進行代理,那么可以考慮使用 CGLIB。
補充
靜態(tài)代理和動態(tài)代理的區(qū)別
最大的區(qū)別就是靜態(tài)代理是編譯期確定的,但是動態(tài)代理卻是運行期確定的。
同時,使用靜態(tài)代理模式需要程序員手寫很多代碼,這個過程是比較浪費時間和精力的。一旦需要代理的類中方法比較多,或者需要同時代理多個對象的時候,這無疑會增加很大的復雜度。
反射是動態(tài)代理的實現(xiàn)方式之一。
動態(tài)代理的用途
Java的動態(tài)代理,在日常開發(fā)中可能并不經(jīng)常使用,但是并不代表他不重要。Java的動態(tài)代理的最主要的用途就是應用在各種框架中。因為使用動態(tài)代理可以很方便的運行期生成代理類,通過代理類可以做很多事情,比如AOP,比如過濾器、攔截器等。
在我們平時使用的框架中,像servlet的filter、包括spring提供的aop以及struts2的攔截器都使用了動態(tài)代理功能。我們?nèi)粘?吹降膍ybatis分頁插件,以及日志攔截、事務攔截、權限攔截這些幾乎全部由動態(tài)代理的身影。
Spring AOP的實現(xiàn)方式
Spring AOP中的動態(tài)代理主要有兩種方式,JDK動態(tài)代理和CGLIB動態(tài)代理。
JDK動態(tài)代理通過反射來接收被代理的類,并且要求被代理的類必須實現(xiàn)一個接口。JDK動態(tài)代理的核心是InvocationHandler接口和Proxy類。
如果目標類沒有實現(xiàn)接口,那么Spring AOP會選擇使用CGLIB來動態(tài)代理目標類。
CGLIB(Code Generation Library),是一個代碼生成的類庫,可以在運行時動態(tài)的生成某個類的子類,注意,CGLIB是通過繼承的方式做的動態(tài)代理,因此如果某個類被標記為final,那么它是無法使用CGLIB做動態(tài)代理的。
JDK 動態(tài)代理代碼示例
public class UserServiceImpl implements UserService { @Override public void add() { // TODO Auto-generated method stub System.out.println("--------------------add----------------------"); } } public class MyInvocationHandler implements InvocationHandler { private Object target; public MyInvocationHandler(Object target) { super(); this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 在方法調(diào)用前進行性能監(jiān)控 PerformanceMonior.begin(target.getClass().getName()+"."+method.getName()); // 通過反射調(diào)用目標對象的方法 Object result = method.invoke(target, args); // 在方法調(diào)用后結束性能監(jiān)控 PerformanceMonior.end(); return result; } public Object getProxy(){ return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), target.getClass().getInterfaces(), this); } } public static void main(String[] args) { UserService service = new UserServiceImpl(); MyInvocationHandler handler = new MyInvocationHandler(service); UserService proxy = (UserService) handler.getProxy(); proxy.add(); }
代碼整體解讀:
說人話就是我們通過jdk的動態(tài)代理,對用戶服務的新增用戶方法,追加了一個性能監(jiān)控功能,通過傳入原對象,得到代理對象(傳入原對象,我攔截開啟性能監(jiān)控功能,我再把方法的調(diào)用轉發(fā)(本質(zhì)就是反射機制調(diào)用)給原對象,原對象方法執(zhí)行結束,我結束性能監(jiān)控)。
Cglib動態(tài)代理代碼示例
public class UserServiceImpl implements UserService { @Override public void add() { // TODO Auto-generated method stub System.out.println("--------------------add----------------------"); } } public class CglibProxy implements MethodInterceptor { private Enhancer enhancer = new Enhancer(); public Object getProxy(Class clazz) { // 設置需要創(chuàng)建子類的類 enhancer.setSuperclass(clazz); enhancer.setCallback(this); // 通過字節(jié)碼技術動態(tài)創(chuàng)建子類實例 return enhancer.create(); } // 實現(xiàn)MethodInterceptor接口方法 public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("前置代理"); // 通過代理類調(diào)用父類中的方法 Object result = proxy.invokeSuper(obj, args); System.out.println("后置代理"); return result; } } public class DoCGLib { public static void main(String[] args) { CglibProxy proxy = new CglibProxy(); // 通過生成子類的方式創(chuàng)建代理類 UserServiceImpl proxyImp = (UserServiceImpl)proxy.getProxy(UserServiceImpl.class); proxyImp.add(); } }
總結
到此這篇關于java中動態(tài)代理如何實現(xiàn)的文章就介紹到這了,更多相關java動態(tài)代理實現(xiàn)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
java保證對象在內(nèi)存中唯一性的實現(xiàn)方法
這篇文章主要介紹了java如何保證對象在內(nèi)存中的唯一性,如果創(chuàng)建多個對象的話,可能會引發(fā)出各種各樣的問題,這時,就需要我們保證這個對象在內(nèi)存中的唯一性,需要的朋友可以參考下2019-06-06mybatis攔截器實現(xiàn)數(shù)據(jù)權限項目實踐
本文主要介紹了mybatis攔截器實現(xiàn)數(shù)據(jù)權限項目實踐,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-06-06