Java代理模式與動(dòng)態(tài)代理之間的關(guān)系以及概念
什么是代理模式
代理模式是開發(fā)中常見的一種設(shè)計(jì)模式,使用代理模式可以很好的對(duì)程序進(jìn)行橫向擴(kuò)展。代理,顧名思義就是一個(gè)真實(shí)對(duì)象會(huì)存在一個(gè)代理對(duì)象,并且代理對(duì)象可以替真實(shí)對(duì)象完成相應(yīng)操作,外部通過代理對(duì)象來訪問真實(shí)對(duì)象并且還可以在代理對(duì)象中進(jìn)行額外操作的擴(kuò)展。
代理模式的特征是擁有接口、代理類、被代理類。并且代理類與被代理類同時(shí)實(shí)現(xiàn)該接口。代理類與被代理類之間通常存在一定關(guān)聯(lián),設(shè)計(jì)時(shí)會(huì)在代理類中注冊(cè)一個(gè)被代理類的對(duì)象用于調(diào)用代理類的方法。這也印證了代理對(duì)象依然是執(zhí)行的真實(shí)對(duì)象的方法
代理模式又分為靜態(tài)代理和動(dòng)態(tài)代理
靜態(tài)代理
靜態(tài)代理,關(guān)鍵字靜態(tài)是指在程序運(yùn)行之前編譯時(shí)就已經(jīng)確定了代理類、被代理類、接口。
下面列舉兩個(gè)示例展示靜態(tài)代理:
學(xué)生通過班長(zhǎng)交班費(fèi),班長(zhǎng)作為學(xué)生的代理,學(xué)生是被代理,具有同一行為就是交班費(fèi),這個(gè)行為我們用接口進(jìn)行約束
程序:
// 接口
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元班費(fèi)");
}
}
// 學(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();}
}
測(cè)試:
public static void main(String[] args) {
// 獲取學(xué)生實(shí)例
Student stu = new Student("張三");
// 學(xué)生找交錢給班長(zhǎng) 學(xué)生找到代理
StuProxy stuProxy = new StuProxy(stu);
// 班長(zhǎng)交給老師 代理交錢(交的是學(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();}
}
測(cè)試:
public static void main(String[] args) {
// 創(chuàng)建租客
Renter renter = new Renter();
// 租客找到中介
RentProxy rentProxy = new RentProxy(renter);
// 租客租房
rentProxy.doRent();
}
上述兩個(gè)示例,向代理類中注入被代理對(duì)象的方式都是通過構(gòu)造器注入,當(dāng)然也可以通過set方法注入
下面演示如果需要在已經(jīng)編寫好的代理類的輸出中添加其他操作時(shí)的操作比如打印一個(gè)日志,在不修改源代碼的情況下擴(kuò)展
// 下面是最經(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作為被代理類,擴(kuò)展代理類如下:
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)代理:代理類在程序運(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ì)象,并且可以通過動(dòng)態(tài)代理對(duì)象對(duì)目標(biāo)對(duì)象(真實(shí)對(duì)象)進(jìn)行功能性補(bǔ)強(qiáng)。大白話來講就是,可以在程序運(yùn)行期間額外的對(duì)真實(shí)對(duì)象功能進(jìn)行擴(kuò)展。
此處的動(dòng)態(tài)代理對(duì)象不是通過預(yù)先編寫好的程序生成的,而是運(yùn)行期間由于用戶需求或者說是代碼的指示生成的
動(dòng)態(tài)代理分為兩種:一類是基于接口實(shí)現(xiàn)的動(dòng)態(tài)代理,另一類是基于類的動(dòng)態(tài)代理
基于接口的動(dòng)態(tài)代理–JDK動(dòng)態(tài)代理通過反射完成,基于類實(shí)現(xiàn)的–>cglib
JDK動(dòng)態(tài)代理核心:Proxy類、InvocationHandler接口、要參與代理的目標(biāo)類必須實(shí)現(xiàn)對(duì)應(yīng)接口,比如上述的Student必須實(shí)現(xiàn)Person接口。
繼續(xù)以上述學(xué)生交班費(fèi)為例,更改為動(dòng)態(tài)代理模式:
JDK動(dòng)態(tài)代理中間類:該類需要實(shí)現(xiàn)InvocationHandler接口
// JDK動(dòng)態(tà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)代理類
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)代理中間類對(duì)象
InvocationHandlerProxyStudent proxyStudent = new InvocationHandlerProxyStudent();
proxyStudent.setStu(stu);
// 代理類實(shí)例
Person proxy = (Person) proxyStudent.getProxy();
// 代理類實(shí)例調(diào)用方法
proxy.giveMoney();
}
上述程序,我們利用Proxy來生成代理類:
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
stu.getClass().getInterfaces(),this);
}
Proxy提供了靜態(tài)的獲取Proxy代理類的方法newProxyInstance,三個(gè)參數(shù)分別是1.類加載器 2.代理對(duì)象實(shí)現(xiàn)的接口 3. 調(diào)用處理程序(InvocationHandler)
在InvocationHandler官方文檔就已經(jīng)指出,每一個(gè)代理實(shí)例都關(guān)聯(lián)有一個(gè)InvocationHandler調(diào)用處理程序,所以這里填this即可
在使用代理類的時(shí)候首先我們創(chuàng)建需要被代理的真實(shí)對(duì)象和動(dòng)態(tài)代理中間類的對(duì)象,用set方法將真實(shí)對(duì)象交給中間類中的代理對(duì)象。在調(diào)用上述getProxy方法獲取代理類。動(dòng)態(tài)代理相對(duì)于靜態(tài)代理有些難以理解,這是因?yàn)殪o態(tài)代理的代理類可以在程序中顯式的被看到,而動(dòng)態(tài)代理中的代理類文件是緩存在Java虛擬機(jī),類名叫$Proxy0。
在虛擬機(jī)中生成的代理類中就會(huì)將我們所調(diào)用的方法內(nèi)置,我們?cè)趫?zhí)行proxy.giveMoney()的同時(shí),實(shí)際上是將giveMoney()方法作為參數(shù)傳進(jìn)invoke()的參數(shù)中去,而此時(shí)我們就可以將其他的操作交給invoke,由于所有代理對(duì)象在執(zhí)行時(shí)最終都會(huì)走invoke方法,所以也為我們的開發(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)代理模式過程解析
- 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)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11
BeanFactory和FactoryBean的區(qū)別示例詳解
這篇文章主要為大家介紹了BeanFactory和FactoryBean的區(qū)別示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10
新聞列表的分頁(yè)查詢java代碼實(shí)現(xiàn)
這篇文章主要為大家詳細(xì)介紹了新聞列表的分頁(yè)查詢java代碼實(shí)現(xiàn),感興趣的小伙伴們可以參考一下2016-08-08
Spring Boot實(shí)現(xiàn)自動(dòng)發(fā)送郵件
這篇文章主要為大家詳細(xì)介紹了Spring Boot實(shí)現(xiàn)自動(dòng)發(fā)送郵件,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-02-02
IDEA上運(yùn)行Flink任務(wù)的實(shí)戰(zhàn)教程
這篇文章主要介紹了IDEA上運(yùn)行Flink任務(wù)的實(shí)戰(zhàn)教程,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-10-10

