Java中代理模式有什么用,相信很多沒(méi)有經(jīng)驗(yàn)的人對(duì)此束手無(wú)策,為此本文總結(jié)了問(wèn)題出現(xiàn)的原因和解決方法,通過(guò)這篇文章希望你能解決這個(gè)問(wèn)題。
巴東網(wǎng)站建設(shè)公司創(chuàng)新互聯(lián)建站,巴東網(wǎng)站設(shè)計(jì)制作,有大型網(wǎng)站制作公司豐富經(jīng)驗(yàn)。已為巴東千余家提供企業(yè)網(wǎng)站建設(shè)服務(wù)。企業(yè)網(wǎng)站搭建\外貿(mào)營(yíng)銷網(wǎng)站建設(shè)要多少錢(qián),請(qǐng)找那個(gè)售后服務(wù)好的巴東做網(wǎng)站的公司定做!
代理模式在實(shí)際開(kāi)發(fā)中的遇到的比較多,Spring的AOP還有RPC中用到。學(xué)習(xí)設(shè)計(jì)思想也是很有必要,弄明白其原理,對(duì)日后工作和學(xué)習(xí)中有很大的幫助。
代理模式可以理解為:在不改變?cè)创a的情況下,實(shí)現(xiàn)對(duì)目標(biāo)對(duì)象的功能擴(kuò)展、增強(qiáng)。
代理模式可分為兩種:靜態(tài)代理和動(dòng)態(tài)代理;動(dòng)態(tài)代理有分為JDK代理和cglib代理。
靜態(tài)代理在使用時(shí),需要定義接口或者父類,被代理對(duì)象與代理對(duì)象一起實(shí)現(xiàn)相同的接口或者是繼承相同父類。
拿海淘購(gòu)物舉例,我們需要找海淘平臺(tái)讓他們幫我們?cè)诤M馍虉?chǎng)購(gòu)買物美價(jià)廉的商品,然后付款,收貨。代碼如下:
先定義一個(gè)抽象對(duì)象角色。
public interface Item { void shopping(); }
目標(biāo)(被代理)對(duì)象角色實(shí)現(xiàn)抽象對(duì)象角色。
public class Person implements Item { @Override public void shopping() { System.out.println("購(gòu)物"); } }
代理對(duì)象角色和目標(biāo)(被代理)對(duì)象角色實(shí)現(xiàn)同一接口,代理對(duì)象內(nèi)部含有目標(biāo)(被代理)對(duì)象的引用。
public class PersonProxy implements Item { private Person person; public PersonProxy(Person person) { this.person = person; } public void begin() { System.out.println("登陸海淘平臺(tái),挑選中意的商品"); } public void end() { System.out.println("提交訂單,付款,等待收獲"); } @Override public void shopping() { begin(); person.shopping(); end(); } }
測(cè)試:
public class Ceshi { public static void main(String[] args) { Person person = new Person(); PersonProxy personProxy = new PersonProxy(person); personProxy.shopping(); } }
運(yùn)行結(jié)果:
登陸海淘平臺(tái),挑選中意的商品 購(gòu)物 提交訂單,付款,等待收獲 Process finished with exit code 0
總結(jié):
靜態(tài)代理是我們自己寫(xiě)的代理類,是在運(yùn)行前就編譯好的,只能代理某一類情況,擴(kuò)展起來(lái)不方便,需修改代碼,繼續(xù)增加代理類。
代理類在程序運(yùn)行時(shí)創(chuàng)建的代理方式被成為動(dòng)態(tài)代理。 我們上面靜態(tài)代理的例子中,代理類(Proxy)是自己定義好的,在程序運(yùn)行之前就已經(jīng)編譯完成。然而動(dòng)態(tài)代理,代理類并不是在Java代碼中定義的,而是在運(yùn)行時(shí)根據(jù)我們?cè)贘ava代碼中的“指示”動(dòng)態(tài)生成的。相比于靜態(tài)代理, 動(dòng)態(tài)代理的優(yōu)勢(shì)在于可以很方便的對(duì)代理類的函數(shù)進(jìn)行統(tǒng)一的處理,而不用修改每個(gè)代理類中的方法。
代理類所在包:java.lang.reflect.Proxy,可以看出JDk代理是通過(guò)Java反射實(shí)現(xiàn)的。
JDK實(shí)現(xiàn)代理只需要使用newProxyInstance方法,但是該方法需要接收三個(gè)參數(shù),完整的寫(xiě)法是:
static Object newProxyInstance(ClassLoader loader, Class>[] interfaces,InvocationHandler h )
注意該方法是在Proxy類中是靜態(tài)方法,且接收的三個(gè)參數(shù)依次為:
ClassLoader loader,
:指定當(dāng)前目標(biāo)對(duì)象使用類加載器,獲取加載器的方法是固定的
Class>[] interfaces,
:目標(biāo)對(duì)象實(shí)現(xiàn)的接口的類型,使用泛型方式確認(rèn)類型
InvocationHandler h
:事件處理,執(zhí)行目標(biāo)對(duì)象的方法時(shí),會(huì)觸發(fā)事件處理器的方法,會(huì)把當(dāng)前執(zhí)行目標(biāo)對(duì)象的方法作為參數(shù)傳入
代理步驟:
定義一個(gè)事件管理器類實(shí)現(xiàn)invocationHandle接口,并重寫(xiě)invoke(代理類,被代理的方法,方法的參數(shù)列表)方法。
實(shí)現(xiàn)被代理類及其實(shí)現(xiàn)的接口。
調(diào)用Proxy.newProxyInstance(類加載器,類實(shí)現(xiàn)的接口,事務(wù)處理器對(duì)象);生成一個(gè)代理實(shí)例。
通過(guò)該代理實(shí)例調(diào)用方法。
創(chuàng)建抽象對(duì)象
public interface Animal { void living(); void eating(); }
創(chuàng)建目標(biāo)(被代理)對(duì)象
public class Person implements Animal { @Override public void living() { System.out.println("談戀愛(ài)"); } @Override public void eating() { System.out.println("吃早餐"); } }
創(chuàng)建代理生成器
public class ProxyInstance implements InvocationHandler { private Object target; public Object createProxy(Object target) { this.target = target; Object o = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); return o; } public void begin() { System.out.println("起床"); } public void end() { System.out.println("睡覺(jué)"); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { begin(); Object obj = method.invoke(target, args); end(); return obj; } }
測(cè)試
@Test public void demo() { Person person = new Person(); Animal proxy = (Animal) new ProxyInstance().createProxy(person); proxy.living(); proxy.eating(); }
結(jié)果
起床 談戀愛(ài) 睡覺(jué) 起床 吃早餐 睡覺(jué) Process finished with exit code 0
從上看可以看出,JDK代理,可以對(duì)不同的方法進(jìn)行動(dòng)態(tài)的代理增強(qiáng)。(對(duì)eating和living進(jìn)行增強(qiáng)代理,區(qū)別于靜態(tài)代理,需要手動(dòng)去對(duì)不同的方法進(jìn)行增強(qiáng)代理)
我們?cè)僭囈幌缕渌?/p>
public class Cat implements Animal { @Override public void living() { System.out.println("曬太陽(yáng)"); } @Override public void eating() { System.out.println("吃貓糧"); } } public class Dog implements Animal { @Override public void living() { System.out.println("拆家"); } @Override public void eating() { System.out.println("吃狗糧"); } }
測(cè)試
@Test public void demo1() { Cat cat = new Cat(); Animal proxy = (Animal) new ProxyInstance().createProxy(cat); proxy.living(); proxy.eating(); } @Test public void demo2() { Dog dog = new Dog(); Animal proxy = (Animal) new ProxyInstance().createProxy(dog); proxy.living(); proxy.eating(); }
結(jié)果
起床 曬太陽(yáng) 睡覺(jué) 起床 吃貓糧 睡覺(jué) Process finished with exit code 0 起床 拆家 睡覺(jué) 起床 吃狗糧 睡覺(jué) Process finished with exit code 0
從上面可以看出來(lái),JDK代理可以對(duì)不同的類動(dòng)態(tài)的生成代理對(duì)象。
原理:
JDK代理是通過(guò)實(shí)現(xiàn)接口,并通過(guò)反射根據(jù)類名進(jìn)行動(dòng)態(tài)生成代理對(duì)象,并根據(jù)方法名,由動(dòng)態(tài)生成的代理對(duì)象對(duì)相應(yīng)的方法進(jìn)行增強(qiáng)。(可以參考源碼)
動(dòng)態(tài)代理模式都是要求目標(biāo)對(duì)象是實(shí)現(xiàn)一個(gè)接口的目標(biāo)對(duì)象,但是有時(shí)候目標(biāo)對(duì)象只是一個(gè)單獨(dú)的對(duì)象,并沒(méi)有實(shí)現(xiàn)任何的接口,這個(gè)時(shí)候就可以使用以目標(biāo)對(duì)象子類的方式類實(shí)現(xiàn)代理,這種方法就叫做:cglib代理。
cglib代理,也叫作子類代理,它是在內(nèi)存中構(gòu)建一個(gè)子類對(duì)象從而實(shí)現(xiàn)對(duì)目標(biāo)對(duì)象功能的擴(kuò)展.
JDK的動(dòng)態(tài)代理有一個(gè)限制,就是使用動(dòng)態(tài)代理的對(duì)象必須實(shí)現(xiàn)一個(gè)或多個(gè)接口,如果想代理沒(méi)有實(shí)現(xiàn)接口的類,就可以使用cglib實(shí)現(xiàn).
cglib是一個(gè)強(qiáng)大的高性能的代碼生成包,它可以在運(yùn)行期擴(kuò)展java類與實(shí)現(xiàn)java接口.它廣泛的被許多AOP的框架使用,例如Spring AOP和synaop,為他們提供方法的interception(攔截)
cglib包的底層是通過(guò)使用一個(gè)小而塊的字節(jié)碼處理框架ASM來(lái)轉(zhuǎn)換字節(jié)碼并生成新的類.不鼓勵(lì)直接使用ASM,因?yàn)樗竽惚仨殞?duì)JVM內(nèi)部結(jié)構(gòu)包括class文件的格式和指令集都很熟悉.
cglib子類代理實(shí)現(xiàn)方法:
1. 需要引入cglib的jar文件,Spring的核心包中已經(jīng)包括了cglib功能。
2. 引入功能包后,就可以在內(nèi)存中動(dòng)態(tài)構(gòu)建子類。
3. 代理的類不能為final,否則報(bào)錯(cuò)。
4. 目標(biāo)對(duì)象的方法如果為final/static,就無(wú)法實(shí)現(xiàn)代理。
創(chuàng)建目標(biāo)類,沒(méi)有實(shí)現(xiàn)接口
public class Person { public void living() { System.out.println("談戀愛(ài)"); } }
創(chuàng)建代理對(duì)象
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 CglibProxy implements MethodInterceptor { //目標(biāo)對(duì)象 private Object target; public CglibProxy(Object target) { this.target = target; } //生成代理對(duì)象的方法 public Object createProxy(){ Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(target.getClass()); enhancer.setCallback(this); Object o = enhancer.create(); return o; } //在目標(biāo)方法前加強(qiáng) public void begin() { System.out.println("起床"); } //在目標(biāo)方法后加強(qiáng) public void end() { System.out.println("睡覺(jué)"); } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { begin(); //代理執(zhí)行目標(biāo)方法 Object invoke = method.invoke(target, objects); end(); return invoke; } }
測(cè)試
public class Demo { @Test public void test(){ Person p = new Person(); Person proxy = (Person) new CglibProxy(p).createProxy(); proxy.living(); } }
結(jié)果
起床 談戀愛(ài) 睡覺(jué) Process finished with exit code 0
再給Person類添加其他方法
public class Person { public void living() { System.out.println("談戀愛(ài)"); } public void working(){ System.out.println("去公司上班"); } }
測(cè)試
public class Demo { @Test public void test(){ Person p = new Person(); Person proxy = (Person) new CglibProxy(p).createProxy(); proxy.living(); proxy.working(); } }
結(jié)果
起床 談戀愛(ài) 睡覺(jué) 起床 去公司上班 睡覺(jué) Process finished with exit code 0
我們?cè)谠囈幌缕渌念?,看看能不能一樣?shí)現(xiàn)代理
public class Cat { public void living(){ System.out.println("曬太陽(yáng)"); } public void working(){ System.out.println("抓老鼠"); } }
測(cè)試
public class Demo { @Test public void test(){ Person p = new Person(); Person proxy = (Person) new CglibProxy(p).createProxy(); proxy.living(); proxy.working(); } @Test public void test1(){ Cat cat = new Cat(); Cat proxy = (Cat) new CglibProxy(cat).createProxy(); proxy.living(); proxy.working(); } }
結(jié)果
起床 曬太陽(yáng) 睡覺(jué) 起床 抓老鼠 睡覺(jué) Process finished with exit code 0
看完上述內(nèi)容,你們掌握J(rèn)ava中代理模式有什么用的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝各位的閱讀!