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