事例
小張是一個普普通通的碼農(nóng)帮孔,每天勤勤懇懇地碼代碼。某天中午小張剛要去吃飯不撑,一個電話打到了他的手機(jī)上文兢。“是XX公司的小張嗎焕檬?我是YY公司的王AA”姆坚。“哦实愚,是王總啊兼呵,有什么事情嗎烹俗?”。溝通過后萍程,小張弄明白了,原來客戶有個需求,剛好負(fù)責(zé)這方面開發(fā)的是小張兔仰,客戶就直接找到了他茫负。不過小張卻沒有答應(yīng)客戶的請求,而是讓客戶找產(chǎn)品經(jīng)理小李溝通乎赴。
是小張著急去吃面而甩鍋嗎忍法?并不是,只是為了使故事可以套到代理模式上榕吼。我們先看一下代理模式的定義: * 為其他對象提供一種代理饿序,以控制對這個對象的訪問。(Provide a surrogate or placeholder for another object to control access to it)
對照定義羹蚣,碼農(nóng)小張可以映射為其他對象原探,產(chǎn)品經(jīng)理小李為小張的代理。我們通過JAVA代碼顽素,表述上面事例咽弦。
靜態(tài)代理
1.抽象角色
基于面向?qū)ο蟮乃枷耄紫榷x一個碼農(nóng)接口,它有一個實(shí)現(xiàn)用戶需求的方法胁出。
publicinterfaceICoder {
????publicvoidimplDemands(String demandName);
}
2.真實(shí)角色
我們假設(shè)小張是JAVA程序員型型,定義一個JAVA碼農(nóng)類,他通過JAA語言實(shí)現(xiàn)需求全蝶。
publicclassJavaCoder implementsICoder{
????privateString name;
????publicJavaCoder(String name){
????????this.name = name;
????}
????@Override
????publicvoidimplDemands(String demandName) {
????????System.out.println(name + " implemented demand:"+ demandName + " in JAVA!");
????}
}
3.代理角色
委屈一下產(chǎn)品經(jīng)理闹蒜,將其命名為碼農(nóng)代理類,同時讓他實(shí)現(xiàn)ICoder接口抑淫。
publicclassCoderProxy implementsICoder{
????privateICoder coder;
????publicCoderProxy(ICoder coder){
????????this.coder = coder;
????}
????@Override
????publicvoidimplDemands(String demandName) {
????????coder.implDemands(demandName);
????}
}
上面一個接口绷落,兩個類,就實(shí)現(xiàn)了代理模式始苇。Are you kidding me嘱函?這么簡單?是的埂蕊,就是這么簡單往弓。 我們通過一個場景類,模擬用戶找產(chǎn)品經(jīng)理增加需求蓄氧。
publicclassCustomer {
????publicstaticvoidmain(String args[]){
????????//定義一個java碼農(nóng)
????????ICoder coder = newJavaCoder("Zhang");
????????//定義一個產(chǎn)品經(jīng)理
????????ICoder proxy = newCoderProxy(coder);
????????//讓產(chǎn)品經(jīng)理實(shí)現(xiàn)一個需求
????????proxy.implDemands();
????}
}
運(yùn)行程序函似,結(jié)果如下:
1Zhang implemented demand:Add user manageMent in JAVA!
產(chǎn)品經(jīng)理充當(dāng)了程序員的代理,客戶把需求告訴產(chǎn)品經(jīng)理喉童,并不需要和程序員接觸撇寞。看到這里,有些機(jī)智的程序員發(fā)現(xiàn)了問題蔑担。你看牌废,產(chǎn)品經(jīng)理就把客戶的需求轉(zhuǎn)達(dá)了一下,怪不得我看產(chǎn)品經(jīng)理這么不爽啤握。
產(chǎn)品經(jīng)理當(dāng)然不只是轉(zhuǎn)達(dá)用戶需求鸟缕,他還有很多事情可以做。比如排抬,該項(xiàng)目決定不接受新增功能的需求了懂从,對修CoderProxy類做一些修改:
publicclassCoderProxy implementsICoder{
????privateICoder coder;
????publicCoderProxy(ICoder coder){
????????this.coder = coder;
????}
????@Override
????publicvoidimplDemands(String demandName) {
????????if(demandName.startsWith("Add")){
????????????System.out.println("No longer receive 'Add' demand");
????????????return;
????????}
????????coder.implDemands(demandName);
????}
}
這樣,當(dāng)客戶再有增加功能的需求時蹲蒲,產(chǎn)品經(jīng)理就直接回絕了番甩,程序員無需再對這部分需求做過濾。
總結(jié)
我們對上面的事例做一個簡單的抽象:
代理模式包含如下角色:
Subject:抽象主題角色届搁≡笛Γ可以是接口,也可以是抽象類卡睦。
RealSubject:真實(shí)主題角色掩宜。業(yè)務(wù)邏輯的具體執(zhí)行者。
ProxySubject:代理主題角色么翰。內(nèi)部含有RealSubject的引用,負(fù)責(zé)對真實(shí)角色的調(diào)用牺汤,并在真實(shí)主題角色處理前后做預(yù)處理和善后工作。
代理模式優(yōu)點(diǎn):
職責(zé)清晰 真實(shí)角色只需關(guān)注業(yè)務(wù)邏輯的實(shí)現(xiàn)浩嫌,非業(yè)務(wù)邏輯部分檐迟,后期通過代理類完成即可。
高擴(kuò)展性 不管真實(shí)角色如何變化码耐,由于接口是固定的追迟,代理類無需做任何改動。
動態(tài)代理
前面講的主要是靜態(tài)代理骚腥。那么什么是動態(tài)代理呢敦间?
假設(shè)有這么一個需求,在方法執(zhí)行前和執(zhí)行完成后束铭,打印系統(tǒng)時間廓块。這很簡單嘛,非業(yè)務(wù)邏輯契沫,只要在代理類調(diào)用真實(shí)角色的方法前带猴、后輸出時間就可以了。像上例懈万,只有一個implDemands方法拴清,這樣實(shí)現(xiàn)沒有問題靶病。但如果真實(shí)角色有10個方法,那么我們要寫10遍完全相同的代碼口予。有點(diǎn)追求的碼農(nóng)娄周,肯定會對這種方法感到非常不爽。有些機(jī)智的小伙伴可能想到了用AOP解決這個問題沪停。非常正確煤辨。莫非AOP和動態(tài)代理有什么關(guān)系?沒錯牙甫!AOP用的恰恰是動態(tài)代理。
代理類在程序運(yùn)行時創(chuàng)建的代理方式被稱為動態(tài)代理调违。也就是說窟哺,代理類并不需要在Java代碼中定義,而是在運(yùn)行時動態(tài)生成的技肩。相比于靜態(tài)代理且轨, 動態(tài)代理的優(yōu)勢在于可以很方便的對代理類的函數(shù)進(jìn)行統(tǒng)一的處理,而不用修改每個代理類的函數(shù)虚婿。對于上例打印時間的需求旋奢,通過使用動態(tài)代理,我們可以做一個“統(tǒng)一指示”然痊,對所有代理類的方法進(jìn)行統(tǒng)一處理至朗,而不用逐一修改每個方法。下面我們來具體介紹下如何使用動態(tài)代理方式實(shí)現(xiàn)我們的需求剧浸。
與靜態(tài)代理相比锹引,抽象角色、真實(shí)角色都沒有變化唆香。變化的只有代理類嫌变。因此,抽象角色躬它、真實(shí)角色腾啥,參考ICoder和JavaCodr。
在使用動態(tài)代理時冯吓,我們需要定義一個位于代理類與委托類之間的中介類倘待,也叫動態(tài)代理類,這個類被要求實(shí)現(xiàn)InvocationHandler接口:
public class CoderDynamicProxy implements InvocationHandler{
?????//被代理的實(shí)例
????private ICoder coder;
????public CoderDynamicProxy(ICoder _coder){
????????this.coder = _coder;
????}
????//調(diào)用被代理的方法
????@Override
????public Object invoke(Object proxy, Method method, Object[] args) throwsThrowable {
????????System.out.println(System.currentTimeMillis());
????????Object result = method.invoke(coder, args);
????????System.out.println(System.currentTimeMillis());
????????return result;
????}
}
當(dāng)我們調(diào)用代理類對象的方法時组贺,這個“調(diào)用”會轉(zhuǎn)送到中介類的invoke方法中延柠,參數(shù)method標(biāo)識了我們具體調(diào)用的是代理類的哪個方法,args為這個方法的參數(shù)锣披。
我們通過一個場景類贞间,模擬用戶找產(chǎn)品經(jīng)理更改需求贿条。
public class DynamicClient {
?????public static void main(String args[]){
????????????//要代理的真實(shí)對象
????????????ICoder coder = new JavaCoder("Zhang");
????????????//創(chuàng)建中介類實(shí)例
????????????InvocationHandler? handler = new CoderDynamicProxy(coder);
????????????//獲取類加載器
????????????ClassLoader cl = coder.getClass().getClassLoader();
????????????//動態(tài)產(chǎn)生一個代理類
????????????ICoder proxy = (ICoder) Proxy.newProxyInstance(cl, coder.getClass().getInterfaces(), handler);
????????????//通過代理類,執(zhí)行doSomething方法增热;
????????????proxy.implDemands("Modify user management");
????????}
}
執(zhí)行結(jié)果如下:
1501728574978
Zhang implemented demand:Modify user management in JAVA!
1501728574979
通過上述代碼整以,就實(shí)現(xiàn)了,在執(zhí)行委托類的所有方法前峻仇、后打印時間公黑。還是那個熟悉的小張,但我們并沒有創(chuàng)建代理類摄咆,也沒有時間ICoder接口凡蚜。這就是動態(tài)代理。
總結(jié)
總結(jié)一下吭从,一個典型的動態(tài)代理可分為以下四個步驟:
創(chuàng)建抽象角色
創(chuàng)建真實(shí)角色
通過實(shí)現(xiàn)InvocationHandler接口創(chuàng)建中介類
通過場景類朝蜘,動態(tài)生成代理類
如果只是想用動態(tài)代理,看到這里就夠了涩金。但如果想知道為什么通過proxy對象谱醇,就能夠執(zhí)行中介類的invoke方法,以及生成的proxy對象是什么樣的步做,可以繼續(xù)往下看副渴。
源碼分析(JDK7)
看到這里的小伙伴,都是有追求的程序員全度。上面的場景類中煮剧,通過
//動態(tài)產(chǎn)生一個代理類
ICoder proxy = (ICoder) Proxy.newProxyInstance(cl, coder.getClass().getInterfaces(), handler);
動態(tài)產(chǎn)生了一個代理類。那么這個代理類是如何產(chǎn)生的呢将鸵?我們通過代碼一窺究竟轿秧。
Proxy類的newProxyInstance方法,主要業(yè)務(wù)邏輯如下:
//生成代理類class咨堤,并加載到j(luò)vm中
Class cl = getProxyClass0(loader, interfaces);
//獲取代理類參數(shù)為InvocationHandler的構(gòu)造函數(shù)
finalConstructor cons = cl.getConstructor(constructorParams);
//生成代理類菇篡,并返回
returnnewInstance(cons, ih);
上面代碼做了三件事:
根據(jù)傳入的參數(shù)interfaces動態(tài)生成一個類,它實(shí)現(xiàn)interfaces中的接口一喘,該例中即ICoder接口的implDemands方法驱还。假設(shè)動態(tài)生成的類為$Proxy0。
通過傳入的classloder,將剛生成的$Proxy0類加載到j(luò)vm中凸克。
利用中介類议蟆,調(diào)用$Proxy0的$Proxy0(InvocationHandler)構(gòu)造函數(shù),創(chuàng)建$Proxy0類的實(shí)例萎战,其InvocationHandler屬性咐容,為我們創(chuàng)建的中介類。
上面的核心蚂维,就在于getProxyClass0方法:
privatestaticClass getProxyClass0(ClassLoader loader,
???????????????????????????????????????????Class... interfaces) {
????????if(interfaces.length > 65535) {
????????????thrownewIllegalArgumentException("interface limit exceeded");
????????}
????????// If the proxy class defined by the given loader implementing
????????// the given interfaces exists, this will simply return the cached copy;
????????// otherwise, it will create the proxy class via the ProxyClassFactory
????????returnproxyClassCache.get(loader, interfaces);
????}
在Proxy類中有個屬性proxyClassCache戳粒,這是一個WeakCache類型的靜態(tài)變量路狮。它指示了類加載器和代理類之間的映射。所以proxyClassCache的get方法用于根據(jù)類加載器來獲取Proxy類蔚约,如果已經(jīng)存在則直接從cache中返回奄妨,如果沒有則創(chuàng)建一個映射并更新cache表。
我們跟一下代理類的創(chuàng)建流程:
調(diào)用Factory類的get方法苹祟,而它又調(diào)用了ProxyClassFactory類的apply方法砸抛,最終找到下面一行代碼:
1
2
//Generate the specified proxy class.
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces);
就是它,生成了代理類树枫。
查看動態(tài)生成的代理類
通過上面的分析直焙,我們已經(jīng)知道Proxy類動態(tài)創(chuàng)建代理類的流程。那創(chuàng)建出來的代理類到底是什么樣子的呢砂轻?我們可以通過下面的代碼奔誓,手動生成:
publicclassCodeUtil {
???????publicstaticvoidmain(String[] args) throwsIOException {
????????????byte[] classFile = ProxyGenerator.generateProxyClass("TestProxyGen", JavaCoder.class.getInterfaces());
????????????File file = newFile("D:/aaa/TestProxyGen.class");
????????????FileOutputStream fos = newFileOutputStream(file);
????????????fos.write(classFile);
????????????fos.flush();
????????????fos.close();
??????????}
?}
通過反編譯工具查看生成的class文件:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import model.proxy.ICoder;
public final classTestProxyGen extends Proxy
??implementsICoder
{
??privatestaticMethod m1;
??privatestaticMethod m0;
??privatestaticMethod m3;
??privatestaticMethod m2;
??publicTestProxyGen(InvocationHandler paramInvocationHandler)
????throws
??{
????super(paramInvocationHandler);
??}
??publicfinalbooleanequals(Object paramObject)
????throws
??{
????try
????{
??????return((Boolean)this.h.invoke(this, m1, newObject[] { paramObject })).booleanValue();
????}
????catch(RuntimeException localRuntimeException)
????{
??????throwlocalRuntimeException;
????}
????catch(Throwable localThrowable)
????{
????}
????thrownewUndeclaredThrowableException(localThrowable);
??}
??publicfinalinthashCode()
????throws
??{
????try
????{
??????return((Integer)this.h.invoke(this, m0, null)).intValue();
????}
????catch(RuntimeException localRuntimeException)
????{
??????throwlocalRuntimeException;
????}
????catch(Throwable localThrowable)
????{
????}
????thrownewUndeclaredThrowableException(localThrowable);
??}
??publicfinalvoidimplDemands(String paramString)
????throws
??{
????try
????{
??????this.h.invoke(this, m3, newObject[] { paramString });
??????return;
????}
????catch(RuntimeException localRuntimeException)
????{
??????throwlocalRuntimeException;
????}
????catch(Throwable localThrowable)
????{
????}
????thrownewUndeclaredThrowableException(localThrowable);
??}
??publicfinalString toString()
????throws
??{
????try
????{
??????return(String)this.h.invoke(this, m2, null);
????}
????catch(RuntimeException localRuntimeException)
????{
??????throwlocalRuntimeException;
????}
????catch(Throwable localThrowable)
????{
????}
????thrownewUndeclaredThrowableException(localThrowable);
??}
??static
??{
????try
????{
??????m1 = Class.forName("java.lang.Object").getMethod("equals", newClass[] { Class.forName("java.lang.Object") });
??????m0 = Class.forName("java.lang.Object").getMethod("hashCode", newClass[0]);
??????m3 = Class.forName("model.proxy.ICoder").getMethod("implDemands", newClass[] { Class.forName("java.lang.String") });
??????m2 = Class.forName("java.lang.Object").getMethod("toString", newClass[0]);
??????return;
????}
????catch(NoSuchMethodException localNoSuchMethodException)
????{
??????thrownewNoSuchMethodError(localNoSuchMethodException.getMessage());
????}
????catch(ClassNotFoundException localClassNotFoundException)
????{
????}
????thrownewNoClassDefFoundError(localClassNotFoundException.getMessage());
??}
}
這樣,我們就理解舔清,為什么調(diào)用代理類的implDemands方法丝里,回去執(zhí)行中介類的invoke方法了曲初。