本篇文章繼續(xù)介紹Java反射機制,不同的是側(cè)重于介紹動態(tài)代理。動態(tài)代理是代理模式中的一種,是通過Java反射機制來實現(xiàn)的。因此本篇文章先介紹代理模式,然后介紹Java反射機制與動態(tài)代理。
一、代理模式
定義:給某個對象提供一個代理對象,并由代理對象控制對于原對象的訪問,即客戶不直接操控原對象,而是通過代理對象間接地操控原對象。
1、代理模式的理解
代理模式使用代理對象完成用戶請求,屏蔽用戶對真實對象的訪問。現(xiàn)實世界的代理人被授權(quán)執(zhí)行當(dāng)事人的一些事宜,無需當(dāng)事人出面,從第三方的角度看,似乎當(dāng)事人并不存在,因為他只和代理人通信。而事實上代理人是要有當(dāng)事人的授權(quán),并且在核心問題上還需要請示當(dāng)事人。在軟件設(shè)計中,使用代理模式的意圖也很多,比如因為安全原因需要屏蔽客戶端直接訪問真實對象,或者在遠程調(diào)用中需要使用代理類處理遠程方法調(diào)用的技術(shù)細節(jié),也可能為了提升系統(tǒng)性能,對真實對象進行封裝,從而達到延遲加載的目的。
2、代理模式的參與者
代理模式的角色分四種:
主題接口: Subject 是委托對象和代理對象都共同實現(xiàn)的接口,即代理類的所實現(xiàn)的行為接口。Request() 是委托對象和代理對象共同擁有的方法。
目標(biāo)對象:ReaSubject 是原對象,也就是被代理的對象。
代理對象: Proxy 是代理對象,用來封裝真是主題類的代理類。
客戶端 :使用代理類和主題接口完成一些工作。
3、代理模式的分類
代理的實現(xiàn)分為:
靜態(tài)代理:代理類是在編譯時就實現(xiàn)好的。也就是說 Java 編譯完成后代理類是一個實際的 class 文件。
動態(tài)代理:代理類是在運行時生成的。也就是說 Java 編譯完之后并沒有實際的 class 文件,而是在運行時動態(tài)生成的類字節(jié)碼,并加載到JVM中。
4、代理模式的實現(xiàn)思路
1.代理對象和目標(biāo)對象均實現(xiàn)同一個行為接口。
2.代理類和目標(biāo)類分別具體實現(xiàn)接口邏輯。
3.在代理類的構(gòu)造函數(shù)中實例化一個目標(biāo)對象。
4.在代理類中調(diào)用目標(biāo)對象的行為接口。
5.客戶端想要調(diào)用目標(biāo)對象的行為接口,只能通過代理類來操作。
5、靜態(tài)代理模式的簡單實現(xiàn)
public class ProxyDemo {
public static void main(String args[]){
RealSubject subject = new RealSubject();
Proxy p = new Proxy(subject);
p.request();
}
}
interface Subject{
void request();
}
class RealSubject implements Subject{
public void request(){
System.out.println("request");
}
}
class Proxy implements Subject{
private Subject subject;
public Proxy(Subject subject){
this.subject = subject;
}
public void request(){
System.out.println("PreProcess");
subject.request();
System.out.println("PostProcess");
}
}
目標(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),修改源碼不僅非常麻煩、難以維護,而且會使代碼顯得十分臃腫。
這時候就輪到代理模式上場了,它可以在被調(diào)用方法前后加上自己的操作,而不需要更改被調(diào)用類的源碼,大大地降低了模塊之間的耦合性,體現(xiàn)了極大的優(yōu)勢。
靜態(tài)代理比較簡單,上面的簡單實例就是靜態(tài)代理的應(yīng)用方式,下面介紹本篇文章的主題:動態(tài)代理。
二、Java反射機制與動態(tài)代理
動態(tài)代理的思路和上述思路一致,下面主要講解如何實現(xiàn)。
1、動態(tài)代理介紹
動態(tài)代理是指在運行時動態(tài)生成代理類。即,代理類的字節(jié)碼將在運行時生成并載入當(dāng)前代理的 ClassLoader。與靜態(tài)處理類相比,動態(tài)類有諸多好處。
①不需要為(RealSubject )寫一個形式上完全一樣的封裝類,假如主題接口(Subject)中的方法很多,為每一個接口寫一個代理方法也很麻煩。如果接口有變動,則目標(biāo)對象和代理類都要修改,不利于系統(tǒng)維護;
②使用一些動態(tài)代理的生成方法甚至可以在運行時制定代理類的執(zhí)行邏輯,從而大大提升系統(tǒng)的靈活性。
2、動態(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ù)與上述方法一致。
對應(yīng)上述兩種方法創(chuàng)建動態(tài)代理對象的方式:
//創(chuàng)建一個InvocationHandler對象
InvocationHandler handler = new MyInvocationHandler(.args..);
//使用Proxy生成一個動態(tài)代理類
Class proxyClass = Proxy.getProxyClass(RealSubject.class.getClassLoader(),RealSubject.class.getInterfaces(), handler);
//獲取proxyClass類中一個帶InvocationHandler參數(shù)的構(gòu)造器
Constructor constructor = proxyClass.getConstructor(InvocationHandler.class);
//調(diào)用constructor的newInstance方法來創(chuàng)建動態(tài)實例
RealSubject real = (RealSubject)constructor.newInstance(handler);
//創(chuàng)建一個InvocationHandler對象
InvocationHandler handler = new MyInvocationHandler(.args..);
//使用Proxy直接生成一個動態(tài)代理對象
RealSubject real =Proxy.newProxyInstance(RealSubject.class.getClassLoader(),RealSubject.class.getInterfaces(), handler);
newProxyInstance這個方法實際上做了兩件事:第一,創(chuàng)建了一個新的類【代理類】,這個類實現(xiàn)了Class[] interfaces中的所有接口,并通過你指定的ClassLoader將生成的類的字節(jié)碼加載到JVM中,創(chuàng)建Class對象;第二,以你傳入的InvocationHandler作為參數(shù)創(chuàng)建一個代理類的實例并返回。
Proxy 類還有一些靜態(tài)方法,比如:
InvocationHandler getInvocationHandler(Object proxy):獲得代理對象對應(yīng)的調(diào)用處理器對象。
Class getProxyClass(ClassLoader loader, Class[] interfaces):根據(jù)類加載器和實現(xiàn)的接口獲得代理類。
InvocationHandler 接口中有方法:
invoke(Object proxy, Method method, Object[] args)
這個函數(shù)是在代理對象調(diào)用任何一個方法時都會調(diào)用的,方法不同會導(dǎo)致第二個參數(shù)method不同,第一個參數(shù)是代理對象(表示哪個代理對象調(diào)用了method方法),第二個參數(shù)是 Method 對象(表示哪個方法被調(diào)用了),第三個參數(shù)是指定調(diào)用方法的參數(shù)。
3、動態(tài)代理模式的簡單實現(xiàn)
public class DynamicProxyDemo {
public static void main(String[] args) {
//1.創(chuàng)建目標(biāo)對象
RealSubject realSubject = new RealSubject();
//2.創(chuàng)建調(diào)用處理器對象
ProxyHandler handler = new ProxyHandler(realSubject);
//3.動態(tài)生成代理對象
Subject proxySubject = (Subject)Proxy.newProxyInstance(RealSubject.class.getClassLoader(),
RealSubject.class.getInterfaces(), handler);
//4.通過代理對象調(diào)用方法
proxySubject.request();
}
}
/**
- 主題接口
*/
interface Subject{
void request();
}
/**
- 目標(biāo)對象類
/
class RealSubject implements Subject{
public void request(){
System.out.println("====RealSubject Request====");
}
}
/* - 代理類的調(diào)用處理器
*/
class ProxyHandler implements InvocationHandler{
private Subject subject;
public ProxyHandler(Subject subject){
this.subject = subject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//定義預(yù)處理的工作,當(dāng)然你也可以根據(jù) method 的不同進行不同的預(yù)處理工作
System.out.println("====before====");
//調(diào)用RealSubject中的方法
Object result = method.invoke(subject, args);
System.out.println("====after====");
return result;
}
}
可以看到,我們通過newProxyInstance就產(chǎn)生了一個Subject 的實例,即代理類的實例,然后就可以通過Subject .request(),就會調(diào)用InvocationHandler中的invoke()方法,傳入方法Method對象,以及調(diào)用方法的參數(shù),通過Method.invoke調(diào)用RealSubject中的方法的request()方法。同時可以在InvocationHandler中的invoke()方法加入其他執(zhí)行邏輯。
以上就是代理模式及動態(tài)代理的內(nèi)容。下篇文章將介紹Java反射機制與泛型。