原文地址:http://www.itdecent.cn/p/57dc69b9c418
qq群:614530228
代理模式
定義:給某個對象提供一個代理對象,并由代理對象控制對于原對象的訪問,即客戶不直接操控原對象,而是通過代理對象間接地操控原對象。
代理模式的理解
代理模式使用代理對象完成用戶請求,屏蔽用戶對真實對象的訪問?,F(xiàn)實世界的代理人被授權(quán)執(zhí)行當(dāng)事人的一些事宜,無需當(dāng)事人出面,從第三方的角度看,似乎當(dāng)事人并不存在,因為他只和代理人通信。而事實上代理人是要有當(dāng)事人的授權(quán),并且在核心問題上還需要請示當(dāng)事人。
在軟件設(shè)計中,使用代理模式的意圖也很多,比如因為安全原因需要屏蔽客戶端直接訪問真實對象,或者在遠(yuǎn)程調(diào)用中需要使用代理類處理遠(yuǎn)程方法調(diào)用的技術(shù)細(xì)節(jié),也可能為了提升系統(tǒng)性能,對真實對象進(jìn)行封裝,從而達(dá)到延遲加載的目的。
代理模式的參與者
代理模式的角色分四種:

主題接口:Subject 是委托對象和代理對象都共同實現(xiàn)的接口,即代理類的所實現(xiàn)的行為接口。Request() 是委托對象和代理對象共同擁有的方法。
目標(biāo)對象:RealSubject 是原對象,也就是被代理的對象。
代理對象:Proxy 是代理對象,用來封裝真是主題類的代理類。
客戶端:使用代理類和主題接口完成一些工作。
代理模式的分類
代理的實現(xiàn)分為:
靜態(tài)代理:代理類是在編譯時就實現(xiàn)好的。也就是說 Java 編譯完成后代理類是一個實際的 class 文件。
動態(tài)代理:代理類是在運行時生成的。也就是說 Java 編譯完之后并沒有實際的class 文件,而是在運行時動態(tài)生成的類字節(jié)碼,并加載到JVM中。
動態(tài)代理的實現(xiàn)思路
- 代理對象和目標(biāo)對象均實現(xiàn)同一個行為接口。
- 代理類和目標(biāo)類分別具體實現(xiàn)接口邏輯。
- 在代理類的構(gòu)造函數(shù)中實例化一個目標(biāo)對象。
- 在代理類中調(diào)用目標(biāo)對象的行為接口。
- 客戶端想要調(diào)用目標(biāo)對象的行為接口,只能通過代理類來操作。
靜態(tài)代理模式的簡單實現(xiàn)
public class ProxyDemo {
public static void main(String[] args) {
SubjectProxy proxy = new SubjectProxy(new RealSubject());
proxy.request();
}
}
public interface Subject {
void request();
}
public class RealSubject implements Subject {
@Override
public void request() {
System.out.println("...request...");
}
}
public class SubjectProxy implements Subject {
Subject subject;
public SubjectProxy(Subject subject) {
this.subject = subject;
}
@Override
public void request() {
subject.request();
}
}
目標(biāo)對象(RealSubject )以及代理對象(Proxy)都實現(xiàn)了主題接口(Subject)。在代理對象(Proxy)中,通過構(gòu)造函數(shù)傳入目標(biāo)對象(RealSubject),然后重寫主題接口(Subject)的request()方法,在該方法中調(diào)用目標(biāo)對象(RealSubject )的request()方法,并可以添加一些額外的處理工作在目標(biāo)對象(RealSubject)的request()方法的前后。
代理模式的好處:
假如有這樣的需求,要在某些模塊方法調(diào)用前后加上一些統(tǒng)一的前后處理操作,比如在添加購物車、修改訂單等操作前后統(tǒng)一加上登陸驗證與日志記錄處理,該怎樣實現(xiàn)?首先想到最簡單的就是直接修改源碼,在對應(yīng)模塊的對應(yīng)方法前后添加操作。如果模塊很多,你會發(fā)現(xiàn),修改源碼不僅非常麻煩、難以維護(hù),而且會使代碼顯得十分臃腫。
這時候就輪到代理模式上場了,它可以在被調(diào)用方法前后加上自己的操作,而不需要更改被調(diào)用類的源碼,大大地降低了模塊之間的耦合性,體現(xiàn)了極大的優(yōu)勢。
靜態(tài)代理比較簡單,上面的簡單實例就是靜態(tài)代理的應(yīng)用方式,下面介紹本篇文章的主題:動態(tài)代理。
動態(tài)代理
動態(tài)代理的思路和上述思路一致,下面主要講解如何實現(xiàn)。
動態(tài)代理介紹
動態(tài)代理是指在運行時動態(tài)生成代理類。即,代理類的字節(jié)碼將在運行時生成并載入當(dāng)前代理的 ClassLoader。與靜態(tài)處理類相比,動態(tài)類有諸多好處。
- 不需要為(RealSubject )寫一個形式上完全一樣的封裝類,假如主題接口(Subject)中的方法很多,為每一個接口寫一個代理方法也很麻煩。如果接口有變動,則目標(biāo)對象和代理類都要修改,不利于系統(tǒng)維護(hù);
- 使用一些動態(tài)代理的生成方法甚至可以在運行時制定代理類的執(zhí)行邏輯,從而大大提升系統(tǒng)的靈活性。
動態(tài)代理涉及的主要類
主要涉及兩個類,這兩個類都是java.lang.reflect包下的類,內(nèi)部主要通過反射來實現(xiàn)的。
java.lang.reflect.Proxy:這是生成代理類的主類,通過 Proxy 類生成的代理類都繼承了 Proxy類。Proxy提供了用戶創(chuàng)建動態(tài)代理類和代理對象的靜態(tài)方法,它是所有動態(tài)代理類的父類。
java.lang.reflect.InvocationHandler:這里稱他為"調(diào)用處理器",它是一個接口。當(dāng)調(diào)用動態(tài)代理類中的方法時,將會直接轉(zhuǎn)接到執(zhí)行自定義的InvocationHandler中的invoke()方法。即我們動態(tài)生成的代理類需要完成的具體內(nèi)容需要自己定義一個類,而這個類必須實現(xiàn) InvocationHandler接口,通過重寫invoke()方法來執(zhí)行具體內(nèi)容。
Proxy提供了如下兩個方法來創(chuàng)建動態(tài)代理類和動態(tài)代理實例。
static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) 返
回代理類的java.lang.Class對象。第一個參數(shù)是類加載器對象(即哪個類加載
器來加載這個代理類到 JVM 的方法區(qū)),第二個參數(shù)是接口(表明你這個代
理類需要實現(xiàn)哪些接口),第三個參數(shù)是調(diào)用處理器類實例(指定代理類中具
體要干什么),該代理類將實現(xiàn)interfaces所指定的所有接口,執(zhí)行代理對象的
每個方法時都會被替換執(zhí)行InvocationHandler對象的invoke方法。
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,
InvocationHandler h) 返回代理類實例。參數(shù)與上述方法一致。
public interface UserService {
String getUsername();
int getAge();
}
public class UserServiceImpl implements UserService {
@Override
public String getUsername() {
return "admin";
}
@Override
public int getAge() {
return 20;
}
}
public class UserProxy implements InvocationHandler {
private Object mProxy;
public UserProxy(Object proxy) {
this.mProxy = proxy;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(mProxy, args);
}
}
public class DynamicProxyDemo {
public static void main(String[] args) {
// 第一種方式
UserService userService = new UserServiceImpl();
UserProxy userProxy = new UserProxy(userService);
UserService proxy = (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(), userService.getClass().getInterfaces(), userProxy);
System.out.println(proxy.getUsername() + "..." + proxy.getAge());
// 第二種方式
UserService proxy = (UserService) Proxy.newProxyInstance(UserServiceImpl.class.getClassLoader(), UserServiceImpl.class.getInterfaces(), new UserProxy(new UserServiceImpl()));
System.out.println(proxy.getUsername() + "..." + proxy.getAge());
}
}
The end--