本篇文章繼續(xù)介紹Java反射機(jī)制,不同的是側(cè)重于介紹動(dòng)態(tài)代理。動(dòng)態(tài)代理是代理模式中的一種监氢,是通過(guò)Java反射機(jī)制來(lái)實(shí)現(xiàn)的。因此本篇文章先介紹代理模式藤违,然后介紹Java反射機(jī)制與動(dòng)態(tài)代理浪腐。
一、代理模式
定義:給某個(gè)對(duì)象提供一個(gè)代理對(duì)象顿乒,并由代理對(duì)象控制對(duì)于原對(duì)象的訪(fǎng)問(wèn)议街,即客戶(hù)不直接操控原對(duì)象,而是通過(guò)代理對(duì)象間接地操控原對(duì)象璧榄。
1特漩、代理模式的理解
代理模式使用代理對(duì)象完成用戶(hù)請(qǐng)求,屏蔽用戶(hù)對(duì)真實(shí)對(duì)象的訪(fǎng)問(wèn)∮塘猓現(xiàn)實(shí)世界的代理人被授權(quán)執(zhí)行當(dāng)事人的一些事宜拾稳,無(wú)需當(dāng)事人出面,從第三方的角度看腊脱,似乎當(dāng)事人并不存在访得,因?yàn)樗缓痛砣送ㄐ拧6聦?shí)上代理人是要有當(dāng)事人的授權(quán),并且在核心問(wèn)題上還需要請(qǐng)示當(dāng)事人悍抑。在軟件設(shè)計(jì)中鳄炉,使用代理模式的意圖也很多,比如因?yàn)榘踩蛐枰帘慰蛻?hù)端直接訪(fǎng)問(wèn)真實(shí)對(duì)象搜骡,或者在遠(yuǎn)程調(diào)用中需要使用代理類(lèi)處理遠(yuǎn)程方法調(diào)用的技術(shù)細(xì)節(jié)拂盯,也可能為了提升系統(tǒng)性能,對(duì)真實(shí)對(duì)象進(jìn)行封裝记靡,從而達(dá)到延遲加載的目的谈竿。
2、代理模式的參與者
代理模式的角色分四種:
主題接口: Subject 是委托對(duì)象和代理對(duì)象都共同實(shí)現(xiàn)的接口摸吠,即代理類(lèi)的所實(shí)現(xiàn)的行為接口空凸。Request() 是委托對(duì)象和代理對(duì)象共同擁有的方法。
目標(biāo)對(duì)象:ReaSubject 是原對(duì)象寸痢,也就是被代理的對(duì)象呀洲。
代理對(duì)象: Proxy 是代理對(duì)象,用來(lái)封裝真是主題類(lèi)的代理類(lèi)啼止。
客戶(hù)端 :使用代理類(lèi)和主題接口完成一些工作道逗。
3、代理模式的分類(lèi)
代理的實(shí)現(xiàn)分為:
靜態(tài)代理:代理類(lèi)是在編譯時(shí)就實(shí)現(xiàn)好的献烦。也就是說(shuō) Java 編譯完成后代理類(lèi)是一個(gè)實(shí)際的 class 文件滓窍。
動(dòng)態(tài)代理:代理類(lèi)是在運(yùn)行時(shí)生成的。也就是說(shuō) Java 編譯完之后并沒(méi)有實(shí)際的 class 文件巩那,而是在運(yùn)行時(shí)動(dòng)態(tài)生成的類(lèi)字節(jié)碼贰您,并加載到JVM中。
4拢操、代理模式的實(shí)現(xiàn)思路
1.代理對(duì)象和目標(biāo)對(duì)象均實(shí)現(xiàn)同一個(gè)行為接口。
2.代理類(lèi)和目標(biāo)類(lèi)分別具體實(shí)現(xiàn)接口邏輯舶替。
3.在代理類(lèi)的構(gòu)造函數(shù)中實(shí)例化一個(gè)目標(biāo)對(duì)象令境。
4.在代理類(lèi)中調(diào)用目標(biāo)對(duì)象的行為接口。
5.客戶(hù)端想要調(diào)用目標(biāo)對(duì)象的行為接口顾瞪,只能通過(guò)代理類(lèi)來(lái)操作舔庶。
5、靜態(tài)代理模式的簡(jiǎn)單實(shí)現(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)對(duì)象(RealSubject )以及代理對(duì)象(Proxy)都實(shí)現(xiàn)了主題接口(Subject)陈醒。在代理對(duì)象(Proxy)中惕橙,通過(guò)構(gòu)造函數(shù)傳入目標(biāo)對(duì)象(RealSubject ),然后重寫(xiě)主題接口(Subject)的request()方法钉跷,在該方法中調(diào)用目標(biāo)對(duì)象(RealSubject )的request()方法弥鹦,并可以添加一些額外的處理工作在目標(biāo)對(duì)象(RealSubject )的request()方法的前后。
代理模式的好處:
假如有這樣的需求,要在某些模塊方法調(diào)用前后加上一些統(tǒng)一的前后處理操作彬坏,比如在添加購(gòu)物車(chē)朦促、修改訂單等操作前后統(tǒng)一加上登陸驗(yàn)證與日志記錄處理,該怎樣實(shí)現(xiàn)栓始?首先想到最簡(jiǎn)單的就是直接修改源碼务冕,在對(duì)應(yīng)模塊的對(duì)應(yīng)方法前后添加操作。如果模塊很多幻赚,你會(huì)發(fā)現(xiàn)禀忆,修改源碼不僅非常麻煩、難以維護(hù)落恼,而且會(huì)使代碼顯得十分臃腫箩退。
這時(shí)候就輪到代理模式上場(chǎng)了,它可以在被調(diào)用方法前后加上自己的操作领跛,而不需要更改被調(diào)用類(lèi)的源碼乏德,大大地降低了模塊之間的耦合性,體現(xiàn)了極大的優(yōu)勢(shì)吠昭。
靜態(tài)代理比較簡(jiǎn)單喊括,上面的簡(jiǎn)單實(shí)例就是靜態(tài)代理的應(yīng)用方式,下面介紹本篇文章的主題:動(dòng)態(tài)代理矢棚。
二郑什、Java反射機(jī)制與動(dòng)態(tài)代理
動(dòng)態(tài)代理的思路和上述思路一致,下面主要講解如何實(shí)現(xiàn)蒲肋。
1蘑拯、動(dòng)態(tài)代理介紹
動(dòng)態(tài)代理是指在運(yùn)行時(shí)動(dòng)態(tài)生成代理類(lèi)。即兜粘,代理類(lèi)的字節(jié)碼將在運(yùn)行時(shí)生成并載入當(dāng)前代理的 ClassLoader申窘。與靜態(tài)處理類(lèi)相比,動(dòng)態(tài)類(lèi)有諸多好處孔轴。
①不需要為(RealSubject )寫(xiě)一個(gè)形式上完全一樣的封裝類(lèi)剃法,假如主題接口(Subject)中的方法很多,為每一個(gè)接口寫(xiě)一個(gè)代理方法也很麻煩路鹰。如果接口有變動(dòng)贷洲,則目標(biāo)對(duì)象和代理類(lèi)都要修改,不利于系統(tǒng)維護(hù)晋柱;
②使用一些動(dòng)態(tài)代理的生成方法甚至可以在運(yùn)行時(shí)制定代理類(lèi)的執(zhí)行邏輯优构,從而大大提升系統(tǒng)的靈活性。
2雁竞、動(dòng)態(tài)代理涉及的主要類(lèi)
主要涉及兩個(gè)類(lèi)钦椭,這兩個(gè)類(lèi)都是java.lang.reflect包下的類(lèi),內(nèi)部主要通過(guò)反射來(lái)實(shí)現(xiàn)的。
java.lang.reflect.Proxy: 這是生成代理類(lèi)的主類(lèi)玉凯,通過(guò) Proxy 類(lèi)生成的代理類(lèi)都繼承了 Proxy 類(lèi)势腮。
Proxy提供了用戶(hù)創(chuàng)建動(dòng)態(tài)代理類(lèi)和代理對(duì)象的靜態(tài)方法,它是所有動(dòng)態(tài)代理類(lèi)的父類(lèi)漫仆。
java.lang.reflect.InvocationHandler: 這里稱(chēng)他為"調(diào)用處理器"捎拯,它是一個(gè)接口。當(dāng)調(diào)用動(dòng)態(tài)代理類(lèi)中的方法時(shí)盲厌,將會(huì)直接轉(zhuǎn)接到執(zhí)行自定義的InvocationHandler中的invoke()方法署照。即我們動(dòng)態(tài)生成的代理類(lèi)需要完成的具體內(nèi)容需要自己定義一個(gè)類(lèi),而這個(gè)類(lèi)必須實(shí)現(xiàn) InvocationHandler 接口吗浩,通過(guò)重寫(xiě)invoke()方法來(lái)執(zhí)行具體內(nèi)容建芙。
Proxy提供了如下兩個(gè)方法來(lái)創(chuàng)建動(dòng)態(tài)代理類(lèi)和動(dòng)態(tài)代理實(shí)例。
static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) 返回代理類(lèi)的java.lang.Class對(duì)象懂扼。第一個(gè)參數(shù)是類(lèi)加載器對(duì)象(即哪個(gè)類(lèi)加載器來(lái)加載這個(gè)代理類(lèi)到 JVM 的方法區(qū))禁荸,第二個(gè)參數(shù)是接口(表明你這個(gè)代理類(lèi)需要實(shí)現(xiàn)哪些接口),第三個(gè)參數(shù)是調(diào)用處理器類(lèi)實(shí)例(指定代理類(lèi)中具體要干什么)阀湿,該代理類(lèi)將實(shí)現(xiàn)interfaces所指定的所有接口赶熟,執(zhí)行代理對(duì)象的每個(gè)方法時(shí)都會(huì)被替換執(zhí)行InvocationHandler對(duì)象的invoke方法。
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 返回代理類(lèi)實(shí)例陷嘴。參數(shù)與上述方法一致映砖。
對(duì)應(yīng)上述兩種方法創(chuàng)建動(dòng)態(tài)代理對(duì)象的方式:
//創(chuàng)建一個(gè)InvocationHandler對(duì)象
InvocationHandler handler = new MyInvocationHandler(.args..);
//使用Proxy生成一個(gè)動(dòng)態(tài)代理類(lèi)
Class proxyClass = Proxy.getProxyClass(RealSubject.class.getClassLoader(),RealSubject.class.getInterfaces(), handler);
//獲取proxyClass類(lèi)中一個(gè)帶InvocationHandler參數(shù)的構(gòu)造器
Constructor constructor = proxyClass.getConstructor(InvocationHandler.class);
//調(diào)用constructor的newInstance方法來(lái)創(chuàng)建動(dòng)態(tài)實(shí)例
RealSubject real = (RealSubject)constructor.newInstance(handler);
//創(chuàng)建一個(gè)InvocationHandler對(duì)象
InvocationHandler handler = new MyInvocationHandler(.args..);
//使用Proxy直接生成一個(gè)動(dòng)態(tài)代理對(duì)象
RealSubject real =Proxy.newProxyInstance(RealSubject.class.getClassLoader(),RealSubject.class.getInterfaces(), handler);
newProxyInstance這個(gè)方法實(shí)際上做了兩件事:第一,創(chuàng)建了一個(gè)新的類(lèi)【代理類(lèi)】灾挨,這個(gè)類(lèi)實(shí)現(xiàn)了Class[] interfaces中的所有接口邑退,并通過(guò)你指定的ClassLoader將生成的類(lèi)的字節(jié)碼加載到JVM中,創(chuàng)建Class對(duì)象劳澄;第二地技,以你傳入的InvocationHandler作為參數(shù)創(chuàng)建一個(gè)代理類(lèi)的實(shí)例并返回。
Proxy 類(lèi)還有一些靜態(tài)方法秒拔,比如:
InvocationHandler getInvocationHandler(Object proxy):獲得代理對(duì)象對(duì)應(yīng)的調(diào)用處理器對(duì)象乓土。
Class getProxyClass(ClassLoader loader, Class[] interfaces):根據(jù)類(lèi)加載器和實(shí)現(xiàn)的接口獲得代理類(lèi)。
InvocationHandler 接口中有方法:
invoke(Object proxy, Method method, Object[] args)
這個(gè)函數(shù)是在代理對(duì)象調(diào)用任何一個(gè)方法時(shí)都會(huì)調(diào)用的溯警,方法不同會(huì)導(dǎo)致第二個(gè)參數(shù)method不同,第一個(gè)參數(shù)是代理對(duì)象(表示哪個(gè)代理對(duì)象調(diào)用了method方法)狡相,第二個(gè)參數(shù)是 Method 對(duì)象(表示哪個(gè)方法被調(diào)用了)梯轻,第三個(gè)參數(shù)是指定調(diào)用方法的參數(shù)。
3尽棕、動(dòng)態(tài)代理模式的簡(jiǎn)單實(shí)現(xiàn)
public class DynamicProxyDemo {
public static void main(String[] args) {
//1.創(chuàng)建目標(biāo)對(duì)象
RealSubject realSubject = new RealSubject();
//2.創(chuàng)建調(diào)用處理器對(duì)象
ProxyHandler handler = new ProxyHandler(realSubject);
//3.動(dòng)態(tài)生成代理對(duì)象
Subject proxySubject = (Subject)Proxy.newProxyInstance(RealSubject.class.getClassLoader(),
RealSubject.class.getInterfaces(), handler);
//4.通過(guò)代理對(duì)象調(diào)用方法
proxySubject.request();
}
}
/**
- 主題接口
*/
interface Subject{
void request();
}
/**
- 目標(biāo)對(duì)象類(lèi)
/
class RealSubject implements Subject{
public void request(){
System.out.println("====RealSubject Request====");
}
}
/* - 代理類(lèi)的調(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 的不同進(jìn)行不同的預(yù)處理工作
System.out.println("====before====");
//調(diào)用RealSubject中的方法
Object result = method.invoke(subject, args);
System.out.println("====after====");
return result;
}
}
可以看到,我們通過(guò)newProxyInstance就產(chǎn)生了一個(gè)Subject 的實(shí)例,即代理類(lèi)的實(shí)例伊诵,然后就可以通過(guò)Subject .request()单绑,就會(huì)調(diào)用InvocationHandler中的invoke()方法,傳入方法Method對(duì)象曹宴,以及調(diào)用方法的參數(shù)搂橙,通過(guò)Method.invoke調(diào)用RealSubject中的方法的request()方法。同時(shí)可以在InvocationHandler中的invoke()方法加入其他執(zhí)行邏輯笛坦。
以上就是代理模式及動(dòng)態(tài)代理的內(nèi)容区转。下篇文章將介紹Java反射機(jī)制與泛型。