摘要:動態(tài)代理又被稱為代理或接口代理。靜態(tài)代理在編譯時產(chǎn)生字節(jié)碼文件,可以直接使用,效率高。代理無需實現(xiàn)接口,通過生成類字節(jié)碼實現(xiàn)代理,比反射稍快,不存在性能問題,但會繼承目標對象,需要重寫方法,所以目標對象不能為類。
一、代理模式介紹
代理模式是一種設(shè)計模式,提供了對目標對象額外的訪問方式,即通過代理對象訪問目標對象,這樣可以在不修改原目標對象的前提下,提供額外的功能操作,擴展目標對象的功能。
簡言之,代理模式就是設(shè)置一個中間代理來控制訪問原目標對象,以達到增強原對象的功能和簡化訪問方式。
代理模式UML類圖
舉個例子,我們生活中經(jīng)常到火車站去買車票,但是人一多的話,就會非常擁擠,于是就有了代售點,我們能從代售點買車票了。這其中就是代理模式的體現(xiàn),代售點代理了火車站對象,提供購買車票的方法。
二、靜態(tài)代理這種代理方式需要代理對象和目標對象實現(xiàn)一樣的接口。
優(yōu)點:可以在不修改目標對象的前提下擴展目標對象的功能。
缺點:
冗余。由于代理對象要實現(xiàn)與目標對象一致的接口,會產(chǎn)生過多的代理類。
不易維護。一旦接口增加方法,目標對象與代理對象都要進行修改。
舉例:保存用戶功能的靜態(tài)代理實現(xiàn)
接口類: IUserDao
package com.proxy; public interface IUserDao { public void save(); }
目標對象:UserDao
package com.proxy; public class UserDao implements IUserDao{ @Override public void save() { System.out.println("保存數(shù)據(jù)"); } }
靜態(tài)代理對象:UserDapProxy 需要實現(xiàn)IUserDao接口!
package com.proxy; public class UserDaoProxy implements IUserDao{ private IUserDao target; public UserDaoProxy(IUserDao target) { this.target = target; } @Override public void save() { System.out.println("開啟事務(wù)");//擴展了額外功能 target.save(); System.out.println("提交事務(wù)"); } }
測試類:TestProxy
package com.proxy; import org.junit.Test; public class StaticUserProxy { @Test public void testStaticProxy(){ //目標對象 IUserDao target = new UserDao(); //代理對象 UserDaoProxy proxy = new UserDaoProxy(target); proxy.save(); } }
輸出結(jié)果
開啟事務(wù) 保存數(shù)據(jù) 提交事務(wù)三、動態(tài)代理
動態(tài)代理利用了JDK API,動態(tài)地在內(nèi)存中構(gòu)建代理對象,從而實現(xiàn)對目標對象的代理功能。動態(tài)代理又被稱為JDK代理或接口代理。
靜態(tài)代理與動態(tài)代理的區(qū)別主要在:
靜態(tài)代理在編譯時就已經(jīng)實現(xiàn),編譯完成后代理類是一個實際的class文件
動態(tài)代理是在運行時動態(tài)生成的,即編譯完成后沒有實際的class文件,而是在運行時動態(tài)生成類字節(jié)碼,并加載到JVM中
特點:
動態(tài)代理對象不需要實現(xiàn)接口,但是要求目標對象必須實現(xiàn)接口,否則不能使用動態(tài)代理。
JDK中生成代理對象主要涉及的類有
java.lang.reflect Proxy,主要方法為
static Object newProxyInstance(ClassLoader loader, //指定當前目標對象使用類加載器 Class>[] interfaces, //目標對象實現(xiàn)的接口的類型 InvocationHandler h //事件處理器 ) //返回一個指定接口的代理類實例,該接口可以將方法調(diào)用指派到指定的調(diào)用處理程序。
java.lang.reflect InvocationHandler,主要方法為
Object invoke(Object proxy, Method method, Object[] args) // 在代理實例上處理方法調(diào)用并返回結(jié)果。
舉例:保存用戶功能的動態(tài)代理實現(xiàn)
接口類: IUserDao
package com.proxy; public interface IUserDao { public void save(); }
目標對象:UserDao
package com.proxy; public class UserDao implements IUserDao{ @Override public void save() { System.out.println("保存數(shù)據(jù)"); } }
動態(tài)代理對象:UserProxyFactory
package com.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class ProxyFactory { private Object target;// 維護一個目標對象 public ProxyFactory(Object target) { this.target = target; } // 為目標對象生成代理對象 public Object getProxyInstance() { return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("開啟事務(wù)"); // 執(zhí)行目標對象方法 Object returnValue = method.invoke(target, args); System.out.println("提交事務(wù)"); return null; } }); } }
測試類:TestProxy
package com.proxy; import org.junit.Test; public class TestProxy { @Test public void testDynamicProxy (){ IUserDao target = new UserDao(); System.out.println(target.getClass()); //輸出目標對象信息 IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance(); System.out.println(proxy.getClass()); //輸出代理對象信息 proxy.save(); //執(zhí)行代理方法 } }
輸出結(jié)果
class com.proxy.UserDao class com.sun.proxy.$Proxy4 開啟事務(wù) 保存數(shù)據(jù) 提交事務(wù)四、cglib代理
cglib is a powerful, high performance and quality Code Generation Library. It can extend JAVA classes and implement interfaces at runtime.
cglib (Code Generation Library )是一個第三方代碼生成類庫,運行時在內(nèi)存中動態(tài)生成一個子類對象從而實現(xiàn)對目標對象功能的擴展。
cglib特點
JDK的動態(tài)代理有一個限制,就是使用動態(tài)代理的對象必須實現(xiàn)一個或多個接口。
如果想代理沒有實現(xiàn)接口的類,就可以使用CGLIB實現(xiàn)。
CGLIB是一個強大的高性能的代碼生成包,它可以在運行期擴展Java類與實現(xiàn)Java接口。
它廣泛的被許多AOP的框架使用,例如Spring AOP和dynaop,為他們提供方法的interception(攔截)。
CGLIB包的底層是通過使用一個小而快的字節(jié)碼處理框架ASM,來轉(zhuǎn)換字節(jié)碼并生成新的類。
不鼓勵直接使用ASM,因為它需要你對JVM內(nèi)部結(jié)構(gòu)包括class文件的格式和指令集都很熟悉。
cglib與動態(tài)代理最大的區(qū)別就是
使用動態(tài)代理的對象必須實現(xiàn)一個或多個接口
使用cglib代理的對象則無需實現(xiàn)接口,達到代理類無侵入。
使用cglib需要引入cglib的jar包,如果你已經(jīng)有spring-core的jar包,則無需引入,因為spring中包含了cglib。
cglib的Maven坐標
cglib cglib 3.2.5
舉例:保存用戶功能的動態(tài)代理實現(xiàn)
目標對象:UserDao
package com.cglib; public class UserDao{ public void save() { System.out.println("保存數(shù)據(jù)"); } }
代理對象:ProxyFactory
package com.cglib; import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; public class ProxyFactory implements MethodInterceptor{ private Object target;//維護一個目標對象 public ProxyFactory(Object target) { this.target = target; } //為目標對象生成代理對象 public Object getProxyInstance() { //工具類 Enhancer en = new Enhancer(); //設(shè)置父類 en.setSuperclass(target.getClass()); //設(shè)置回調(diào)函數(shù) en.setCallback(this); //創(chuàng)建子類對象代理 return en.create(); } @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("開啟事務(wù)"); // 執(zhí)行目標對象的方法 Object returnValue = method.invoke(target, args); System.out.println("關(guān)閉事務(wù)"); return null; } }
測試類:TestProxy
package com.cglib; import org.junit.Test; public class TestProxy { @Test public void testCglibProxy(){ //目標對象 UserDao target = new UserDao(); System.out.println(target.getClass()); //代理對象 UserDao proxy = (UserDao) new ProxyFactory(target).getProxyInstance(); System.out.println(proxy.getClass()); //執(zhí)行代理對象方法 proxy.save(); } }
輸出結(jié)果
class com.cglib.UserDao class com.cglib.UserDao$$EnhancerByCGLIB$$552188b6 開啟事務(wù) 保存數(shù)據(jù) 關(guān)閉事務(wù)五、總結(jié)
靜態(tài)代理實現(xiàn)較簡單,只要代理對象對目標對象進行包裝,即可實現(xiàn)增強功能,但靜態(tài)代理只能為一個目標對象服務(wù),如果目標對象過多,則會產(chǎn)生很多代理類。
JDK動態(tài)代理需要目標對象實現(xiàn)業(yè)務(wù)接口,代理類只需實現(xiàn)InvocationHandler接口。
動態(tài)代理生成的類為 lass com.sun.proxy.$Proxy4,cglib代理生成的類為class com.cglib.UserDao$$EnhancerByCGLIB$$552188b6。
靜態(tài)代理在編譯時產(chǎn)生class字節(jié)碼文件,可以直接使用,效率高。
動態(tài)代理必須實現(xiàn)InvocationHandler接口,通過反射代理方法,比較消耗系統(tǒng)性能,但可以減少代理類的數(shù)量,使用更靈活。
cglib代理無需實現(xiàn)接口,通過生成類字節(jié)碼實現(xiàn)代理,比反射稍快,不存在性能問題,但cglib會繼承目標對象,需要重寫方法,所以目標對象不能為final類。
六、相關(guān)資料代理模式相關(guān)知識
代理模式及Java實現(xiàn)動態(tài)代理
設(shè)計模式讀書筆記 - 代理模式
JDK動態(tài)代理實現(xiàn)原理
Java 動態(tài)代理機制分析及擴展
Java代理(jdk靜態(tài)代理、動態(tài)代理和cglib動態(tài)代理)
AOP的底層實現(xiàn)-CGLIB動態(tài)代理和JDK動態(tài)代理
深入淺出CGlib-打造無入侵的類代理
Spring AOP 實現(xiàn)原理與 CGLIB 應(yīng)用
UML相關(guān)知識
博客 - UML類圖與類的關(guān)系詳解
goole圖書 -《UML建模實例詳解》
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/70513.html
Java的三種代理模式 參考:http://www.cnblogs.com/cenyu/...Java核心技術(shù)原書第九版6.5節(jié) 為什么使用代理 我們在寫一個功能函數(shù)時,經(jīng)常需要在其中寫入與功能不是直接相關(guān)但很有必要的代 碼,如日志記錄,信息發(fā)送,安全和事務(wù)支持等,這些枝節(jié)性代碼雖然是必要的,但它會帶來以下麻煩: 枝節(jié)性代碼游離在功能性代碼之外,它不是函數(shù)的目的,這是對OO是一種破壞 枝節(jié)性...
摘要:設(shè)計模式之代理模式今天學到的動態(tài)代理實現(xiàn),對代理這個概念很模糊,看了一篇文章發(fā)現(xiàn)這是一種設(shè)計模式,于是學習記錄一下。簡介代理模式是一種對象結(jié)構(gòu)型的模式,主要為其他對象提供一種代理以控制對這個對象的訪問。下面依次講解著三種代理。 設(shè)計模式之代理模式 今天學到Spring的動態(tài)代理實現(xiàn)AOP,對代理這個概念很模糊,看了一篇文章發(fā)現(xiàn)這是一種設(shè)計模式,于是學習記錄一下。 簡介 代理模式是一種對...
摘要:代理模式代理模式通俗一點的解釋就是在操作一個對象和對象中的方法時,不是直接操作這個對象,還是通過一個代理對象來操作這個實際的目標對象。 代理模式: 代理模式通俗一點的解釋就是在操作一個對象和對象中的方法時,不是直接操作這個對象,還是通過一個代理對象來操作這個實際的目標對象。應(yīng)用場景一般是需要在執(zhí)行某個已經(jīng)寫好的方法前后再添加一段邏輯,比如執(zhí)行方法前打印日志,或者在執(zhí)行方法之前和之后打時...
摘要:代理模式的實現(xiàn)靜態(tài)代理優(yōu)缺點優(yōu)點只對對需要的方法加代理邏輯。通過繼承的方式進行代理,無論目標對象有沒有實現(xiàn)接口都可以代理,但是無法處理的情況。 注意:本文所有的class使用的static修飾主要是為了能在一個類里面測試。實際項目中不應(yīng)該這樣做的,應(yīng)該分包分class。文字描述不是很多,還是看代碼比較好理解吧... 1. Java代理的理解 代理模式是一種設(shè)計模式,簡單說即是在不改變源...
摘要:下面我們通過玩英雄聯(lián)盟代練的例子來說明下登錄游戲贏下了一局英雄聯(lián)盟,獲得了金幣測試結(jié)果登錄游戲贏下了一局英雄聯(lián)盟,獲得了金幣可以這樣理解,自己寫代理類的方式就是靜態(tài)代理。 前言 剛上大學那會,英雄聯(lián)盟火的一塌糊涂,當時每天都想著升到30級開啟排位之旅??墒巧?0級需要大把的時間不說,這時候匹配到的人,水平過于參差不齊,問候你全家的事經(jīng)常發(fā)生,那個時候就想要是能有個代練幫我升到30級該...
閱讀 3319·2021-11-16 11:45
閱讀 2670·2021-09-22 15:23
閱讀 575·2021-07-30 14:58
閱讀 470·2019-08-30 15:54
閱讀 2248·2019-08-29 16:19
閱讀 3028·2019-08-29 12:45
閱讀 949·2019-08-23 17:57
閱讀 1804·2019-08-23 17:54