Java代理模式與動(dòng)態(tài)代理之間的關(guān)系以及概念
什么是代理模式
代理模式是開(kāi)發(fā)中常見(jiàn)的一種設(shè)計(jì)模式,使用代理模式可以很好的對(duì)程序進(jìn)行橫向擴(kuò)展。代理,顧名思義就是一個(gè)真實(shí)對(duì)象會(huì)存在一個(gè)代理對(duì)象,并且代理對(duì)象可以替真實(shí)對(duì)象完成相應(yīng)操作,外部通過(guò)代理對(duì)象來(lái)訪問(wèn)真實(shí)對(duì)象并且還可以在代理對(duì)象中進(jìn)行額外操作的擴(kuò)展。
代理模式的特征是擁有接口、代理類(lèi)、被代理類(lèi)。并且代理類(lèi)與被代理類(lèi)同時(shí)實(shí)現(xiàn)該接口。代理類(lèi)與被代理類(lèi)之間通常存在一定關(guān)聯(lián),設(shè)計(jì)時(shí)會(huì)在代理類(lèi)中注冊(cè)一個(gè)被代理類(lèi)的對(duì)象用于調(diào)用代理類(lèi)的方法。這也印證了代理對(duì)象依然是執(zhí)行的真實(shí)對(duì)象的方法
代理模式又分為靜態(tài)代理和動(dòng)態(tài)代理
靜態(tài)代理
靜態(tài)代理,關(guān)鍵字靜態(tài)是指在程序運(yùn)行之前編譯時(shí)就已經(jīng)確定了代理類(lèi)、被代理類(lèi)、接口。
下面列舉兩個(gè)示例展示靜態(tài)代理:
學(xué)生通過(guò)班長(zhǎng)交班費(fèi),班長(zhǎng)作為學(xué)生的代理,學(xué)生是被代理,具有同一行為就是交班費(fèi),這個(gè)行為我們用接口進(jìn)行約束
程序:
// 接口 public interface Person {void giveMoney();} // 學(xué)生類(lèi)--> 被代理類(lèi) public class Student implements Person{ private String name; public Student(String name) {this.name = name;} public void giveMoney() { System.out.println(name+"同學(xué)上交50元班費(fèi)"); } } // 學(xué)生代理類(lèi) public class StuProxy implements Person{ private Student stu; public StuProxy(Student stu) { if (stu.getClass() == Student.class) { this.stu = stu; } } public void giveMoney() {stu.giveMoney();} }
測(cè)試:
public static void main(String[] args) { // 獲取學(xué)生實(shí)例 Student stu = new Student("張三"); // 學(xué)生找交錢(qián)給班長(zhǎng) 學(xué)生找到代理 StuProxy stuProxy = new StuProxy(stu); // 班長(zhǎng)交給老師 代理交錢(qián)(交的是學(xué)生的錢(qián)) stuProxy.giveMoney(); }
房屋租賃,租客通過(guò)中介找房源,租客為被代理類(lèi),中介為代理類(lèi),接口聲明租房方法
// 接口 public interface Rent {void doRent();} // 被代理類(lèi) public class Renter implements Rent{ public void doRent() { System.out.println("租客租房了"); } } // 代理類(lèi) public class RentProxy implements Rent{ private Renter renter; public RentProxy(Renter renter) { if (renter.getClass()==Renter.class) this.renter = renter; } public void doRent() {renter.doRent();} }
測(cè)試:
public static void main(String[] args) { // 創(chuàng)建租客 Renter renter = new Renter(); // 租客找到中介 RentProxy rentProxy = new RentProxy(renter); // 租客租房 rentProxy.doRent(); }
上述兩個(gè)示例,向代理類(lèi)中注入被代理對(duì)象的方式都是通過(guò)構(gòu)造器注入,當(dāng)然也可以通過(guò)set方法注入
下面演示如果需要在已經(jīng)編寫(xiě)好的代理類(lèi)的輸出中添加其他操作時(shí)的操作比如打印一個(gè)日志,在不修改源代碼的情況下擴(kuò)展
// 下面是最經(jīng)典的Service層的寫(xiě)法 public interface UserService { void del(); void select(); } public class UserServiceImpl implements UserService{ public void del() { System.out.println("刪除操作"); } public void select() { System.out.println("查詢(xún)操作"); } }
假設(shè)現(xiàn)在我們需要在每一次執(zhí)行操作前后打印一次執(zhí)行日志,并且不能修改源代碼那么就可以用到代理模式,將UserServiceImpl作為被代理類(lèi),擴(kuò)展代理類(lèi)如下:
public class UserServiceImplProxy implements UserService{ private UserServiceImpl userService; // set注入 public void setUserService(UserServiceImpl userService) { this.userService = userService; } public void del() { printLog(); userService.del(); } public void select() { printLog(); userService.select(); } private void printLog(){ System.out.println("執(zhí)行時(shí)間="+new Date()); } }
動(dòng)態(tài)代理
動(dòng)態(tài)代理:代理類(lèi)在程序運(yùn)行時(shí)被創(chuàng)建的代理方式。
關(guān)鍵在于動(dòng)態(tài),程序具有了動(dòng)態(tài)特性,可以在運(yùn)行期間根據(jù)不同的目標(biāo)對(duì)象生成動(dòng)態(tài)代理對(duì)象,并且可以通過(guò)動(dòng)態(tài)代理對(duì)象對(duì)目標(biāo)對(duì)象(真實(shí)對(duì)象)進(jìn)行功能性補(bǔ)強(qiáng)。大白話來(lái)講就是,可以在程序運(yùn)行期間額外的對(duì)真實(shí)對(duì)象功能進(jìn)行擴(kuò)展。
此處的動(dòng)態(tài)代理對(duì)象不是通過(guò)預(yù)先編寫(xiě)好的程序生成的,而是運(yùn)行期間由于用戶需求或者說(shuō)是代碼的指示生成的
動(dòng)態(tài)代理分為兩種:一類(lèi)是基于接口實(shí)現(xiàn)的動(dòng)態(tài)代理,另一類(lèi)是基于類(lèi)的動(dòng)態(tài)代理
基于接口的動(dòng)態(tài)代理–JDK動(dòng)態(tài)代理通過(guò)反射完成,基于類(lèi)實(shí)現(xiàn)的–>cglib
JDK動(dòng)態(tài)代理核心:Proxy類(lèi)、InvocationHandler接口、要參與代理的目標(biāo)類(lèi)必須實(shí)現(xiàn)對(duì)應(yīng)接口,比如上述的Student必須實(shí)現(xiàn)Person接口。
繼續(xù)以上述學(xué)生交班費(fèi)為例,更改為動(dòng)態(tài)代理模式:
JDK動(dòng)態(tài)代理中間類(lèi):該類(lèi)需要實(shí)現(xiàn)InvocationHandler接口
// JDK動(dòng)態(tài)代理中間類(lèi) public class ProxyInvocationHandler implements InvocationHandler { // 被代理的對(duì)象 private Object target; // ser方法注入?yún)?shù) public void setTarget(Object target) {this.target = target;} // 生成動(dòng)態(tài)代理類(lèi) public Object getProxy(){ return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(),this); } // 處理代理實(shí)例,并返回結(jié)果 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = method.invoke(target,args); return result; } }
測(cè)試:
public static void main(String[] args) { // 獲取真實(shí)角色 Student stu = new Student("張三三"); // 動(dòng)態(tài)代理中間類(lèi)對(duì)象 InvocationHandlerProxyStudent proxyStudent = new InvocationHandlerProxyStudent(); proxyStudent.setStu(stu); // 代理類(lèi)實(shí)例 Person proxy = (Person) proxyStudent.getProxy(); // 代理類(lèi)實(shí)例調(diào)用方法 proxy.giveMoney(); }
上述程序,我們利用Proxy來(lái)生成代理類(lèi):
public Object getProxy(){ return Proxy.newProxyInstance(this.getClass().getClassLoader(), stu.getClass().getInterfaces(),this); }
Proxy提供了靜態(tài)的獲取Proxy代理類(lèi)的方法newProxyInstance
,三個(gè)參數(shù)分別是1.類(lèi)加載器 2.代理對(duì)象實(shí)現(xiàn)的接口 3. 調(diào)用處理程序(InvocationHandler)
在InvocationHandler官方文檔就已經(jīng)指出,每一個(gè)代理實(shí)例都關(guān)聯(lián)有一個(gè)InvocationHandler調(diào)用處理程序,所以這里填this即可
在使用代理類(lèi)的時(shí)候首先我們創(chuàng)建需要被代理的真實(shí)對(duì)象和動(dòng)態(tài)代理中間類(lèi)的對(duì)象,用set方法將真實(shí)對(duì)象交給中間類(lèi)中的代理對(duì)象。在調(diào)用上述getProxy方法獲取代理類(lèi)。動(dòng)態(tài)代理相對(duì)于靜態(tài)代理有些難以理解,這是因?yàn)殪o態(tài)代理的代理類(lèi)可以在程序中顯式的被看到,而動(dòng)態(tài)代理中的代理類(lèi)文件是緩存在Java虛擬機(jī),類(lèi)名叫$Proxy0。
在虛擬機(jī)中生成的代理類(lèi)中就會(huì)將我們所調(diào)用的方法內(nèi)置,我們?cè)趫?zhí)行proxy.giveMoney()
的同時(shí),實(shí)際上是將giveMoney()方法作為參數(shù)傳進(jìn)invoke()的參數(shù)中去,而此時(shí)我們就可以將其他的操作交給invoke,由于所有代理對(duì)象在執(zhí)行時(shí)最終都會(huì)走invoke方法,所以也為我們的開(kāi)發(fā)節(jié)省大量代碼
到此這篇關(guān)于Java代理模式與動(dòng)態(tài)代理之間的關(guān)系以及概念的文章就介紹到這了,更多相關(guān)Java代理模式與動(dòng)態(tài)代理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Java簡(jiǎn)單實(shí)現(xiàn)動(dòng)態(tài)代理模式過(guò)程解析
- Java代理模式實(shí)例詳解【靜態(tài)代理與動(dòng)態(tài)代理】
- Java動(dòng)態(tài)代理模式的深入揭秘
- Java設(shè)計(jì)模式之動(dòng)態(tài)代理模式實(shí)例分析
- JAVA動(dòng)態(tài)代理模式(從現(xiàn)實(shí)生活角度理解代碼原理)
- java 代理模式及動(dòng)態(tài)代理機(jī)制深入分析
- 詳解java動(dòng)態(tài)代理模式
- java代理模式與動(dòng)態(tài)代理模式詳解
- 代理模式之Java動(dòng)態(tài)代理實(shí)現(xiàn)方法
相關(guān)文章
Spring AOP如何實(shí)現(xiàn)注解式的Mybatis多數(shù)據(jù)源切換詳解
這篇文章主要給大家介紹了關(guān)于Spring AOP如何實(shí)現(xiàn)注解式的Mybatis多數(shù)據(jù)源切換的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11Springboot打包部署項(xiàng)目的過(guò)程詳解
這篇文章主要介紹了Springboot打包部署項(xiàng)目的過(guò)程,通過(guò)jar包方式打包和war方式打包,本文結(jié)合示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2023-12-12idea修改maven模塊名稱(chēng)還顯示老名稱(chēng)問(wèn)題解決
本文主要介紹了idea修改maven模塊名稱(chēng)還顯示老名稱(chēng)問(wèn)題解決,文中通過(guò)圖文介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-11-11BeanFactory和FactoryBean的區(qū)別示例詳解
這篇文章主要為大家介紹了BeanFactory和FactoryBean的區(qū)別示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10新聞列表的分頁(yè)查詢(xún)java代碼實(shí)現(xiàn)
這篇文章主要為大家詳細(xì)介紹了新聞列表的分頁(yè)查詢(xún)java代碼實(shí)現(xiàn),感興趣的小伙伴們可以參考一下2016-08-08Spring Boot實(shí)現(xiàn)自動(dòng)發(fā)送郵件
這篇文章主要為大家詳細(xì)介紹了Spring Boot實(shí)現(xiàn)自動(dòng)發(fā)送郵件,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-02-02Java實(shí)現(xiàn)的猴子吃桃問(wèn)題算法示例
這篇文章主要介紹了Java實(shí)現(xiàn)的猴子吃桃問(wèn)題算法,簡(jiǎn)單描述了猴子吃桃問(wèn)題并結(jié)合實(shí)例形式給出了java解決猴子吃桃問(wèn)題的具體實(shí)現(xiàn)技巧,需要的朋友可以參考下2017-10-10IDEA上運(yùn)行Flink任務(wù)的實(shí)戰(zhàn)教程
這篇文章主要介紹了IDEA上運(yùn)行Flink任務(wù)的實(shí)戰(zhàn)教程,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-10-10