-
動(dòng)態(tài)代理實(shí)現(xiàn)步驟:
創(chuàng)建實(shí)現(xiàn)InvocationHandler的類,必須實(shí)現(xiàn)invoke方法
創(chuàng)建被代理的類以及接口
調(diào)用Proxy的靜態(tài)方法,創(chuàng)建一個(gè)代理類:newProxyInstance
通過(guò)代理調(diào)用方法
查看全部 -
被代理的類及其方法查看全部
-
測(cè)試,創(chuàng)建CglibProxy()代理類型的實(shí)例,調(diào)用代理方法,傳入被代理的?.class?類?;方法中根據(jù)該類型執(zhí)行相關(guān)的邏輯執(zhí)行Train類的實(shí)例化;最后由代理的實(shí)例化對(duì)象調(diào)用move()方法
查看全部 -
代理類的攔截代理方, intercept 中根據(jù)獲得的代理類,有代理類實(shí)現(xiàn)目標(biāo)類的Method
查看全部 -
創(chuàng)建getProxy(Class clazz) 獲取代理類,傳入的參數(shù)是要被代理的類的引用查看全部
-
cglibproxy代理: 實(shí)現(xiàn)MehodInterceptor 方法攔截接口,并實(shí)現(xiàn)intercept(參數(shù)1,餐數(shù)2,參數(shù)3)方法
查看全部 -
產(chǎn)生代理類的方法當(dāng)中業(yè)務(wù)邏輯是固定的,如何動(dòng)態(tài)的實(shí)現(xiàn)指定的業(yè)務(wù)邏輯?
答:可以創(chuàng)建事物處理器,這個(gè)事物處理器可以專門對(duì)某個(gè)對(duì)象的方法來(lái)進(jìn)行處理。
案例:
創(chuàng)建一個(gè)接口,該接口的名稱和JDK的InvocationHandler接口(事物處理器)保持一致,該接口中也定義一個(gè)invoke方法,該方法的作用是對(duì)某個(gè)對(duì)象的方法進(jìn)行處理,所以該方法中需要傳入該方法的對(duì)象,以及方法,這里為了簡(jiǎn)便起見,就不傳遞方法參數(shù)了,如圖。
實(shí)現(xiàn)具體的事物處理:
創(chuàng)建一個(gè)類并實(shí)現(xiàn)InvocationHandler接口,名稱為TimeHandler,實(shí)現(xiàn)接口的方法中,在該方法中實(shí)現(xiàn)傳遞進(jìn)來(lái)對(duì)象的方法。(注意:這里傳遞的是代理的對(duì)象,我們需要傳遞被代理的對(duì)象,并且調(diào)用被代理對(duì)象的方法,所以創(chuàng)建一個(gè)私有屬性O(shè)bject類型的target,并通過(guò)構(gòu)造方法為該屬性賦值)這時(shí)調(diào)用的才是被代理對(duì)象的方法。
在調(diào)用該方法的前后可以增加相應(yīng)的業(yè)務(wù)邏輯。如圖。
這樣事物處理就完成了。這里需要在newProxyInstance(Class infce,InvocationHandler h)方法中增加該接口的參數(shù)。
InvocationHandler參數(shù)傳進(jìn)來(lái)之后,需要在動(dòng)態(tài)產(chǎn)生動(dòng)態(tài)代理類源碼中
,把InvocationHandler傳進(jìn)來(lái),并把該接口引入進(jìn)來(lái),如圖。
這里"private"+infce.getName()+"m;"+rt+就使用不到了就可以刪除掉了。
代理類$Proxy0的構(gòu)造方法的參數(shù)傳進(jìn)來(lái)的參數(shù)是InvocationHandler,
接下來(lái)需要在代理類中調(diào)用InvocationHandler接口的invoke方法,該方法中有兩個(gè)參數(shù),一個(gè)是代理對(duì)象,一個(gè)是代理對(duì)象的方法,代理對(duì)象就是當(dāng)前的對(duì)象this,方法就是當(dāng)前的方法("Method md="+infce.getName()+".getClass().getMethod(\""+m.getName()+"\");。
完整代碼:
測(cè)試:
查看全部 -
此時(shí)已經(jīng)生成java文件,還需要進(jìn)行動(dòng)態(tài)的編譯生成的代理類。
首先需要獲取當(dāng)前系統(tǒng)的java編譯器:
【a】JavaCompiler compiler=ToolProvider.getSystemJavaCompiler();
創(chuàng)建文件的管理者:
【b】StandardJavaFileManager fileMgr=compiler.getStandardFileManager(diagnosticListener,locale,charset),該方法的第一個(gè)參數(shù)是掃描監(jiān)聽器,第二個(gè)和第三個(gè)都是國(guó)際化參數(shù),這里為了簡(jiǎn)單起見都設(shè)置為null。
根據(jù)文件的路徑來(lái)得到管理文件的數(shù)組:
【c】Iterable units=fileMgr.getJavaFileObjects(filename);
獲取編譯的任務(wù):
【d】CompilationTask t=complier.getTask(null,fileMgr,null,null,null,units),第一個(gè)參數(shù)是輸出的位置,這里使用null。第二個(gè)參數(shù)是文件管理者。第三個(gè)參數(shù)是監(jiān)聽器。第四個(gè)和第五個(gè)參數(shù)是國(guó)際化參數(shù)這里都設(shè)置為null。第六個(gè)參數(shù)是要編譯的文件,該方法返回的是編譯的任務(wù)。
接下來(lái)進(jìn)行編譯:
【5】t.call();
最后把文件管理者關(guān)閉掉:
【6】eMgr.close();
這樣就可以對(duì)生成的$Proxy0進(jìn)行編譯(產(chǎn)生$Proxy0.class,說(shuō)明編譯成功,如圖)。
【3】將這個(gè)類加載到內(nèi)存當(dāng)中,產(chǎn)生一個(gè)新的對(duì)象(該對(duì)象就是代理對(duì)象)。
因?yàn)樯傻奈募窃谥付ǖ哪夸浵拢梢酝ㄟ^(guò)ClassLoader進(jìn)行加載:
【a】獲取類加載器:ClassLoader?cl=ClassLoader.getSystemClassLoader();
通過(guò)類加載器加載文件:
【b】Class c=cl.loadClass("com.imooc.proxy.$Proxy0");參數(shù)是相對(duì)路徑。這里為了驗(yàn)證可以通過(guò)c.getName()把加載的類名輸出。
這樣就會(huì)把代理類加載到內(nèi)存當(dāng)中。
【4】return該代理對(duì)象。
產(chǎn)生代理對(duì)象并進(jìn)行返回,創(chuàng)建代理類可以通過(guò)構(gòu)造器進(jìn)行初始化,根據(jù)構(gòu)造器方法傳遞的參數(shù)來(lái)創(chuàng)建該對(duì)象(構(gòu)造器中傳遞的是接口實(shí)現(xiàn)類的對(duì)象):
Constructor ctr=c.getConstructor(infce);
根據(jù)構(gòu)造器創(chuàng)建代理對(duì)象:
ctr.newInstance(new Car());
這樣就創(chuàng)建了動(dòng)態(tài)代理類。
測(cè)試:
查看全部 -
代理查看全部
-
JDK動(dòng)態(tài)代理概念:在代理類和被代理類之間增加了InvocationHandler接口和Proxy類來(lái)動(dòng)態(tài)的產(chǎn)生代理,通過(guò)上節(jié)案例了解JDK動(dòng)態(tài)代理可以對(duì)實(shí)現(xiàn)某些接口的任意類的任意方法產(chǎn)生任意的代理。
通過(guò)案例模擬JDK動(dòng)態(tài)代理的內(nèi)部實(shí)現(xiàn):
實(shí)現(xiàn)Proxy.newProxyInstance(被代理類的類加載器,被代理類實(shí)現(xiàn)的接口,事務(wù)處理器對(duì)象)的功能————思路:創(chuàng)建一個(gè)類,提供一個(gè)靜態(tài)方法,該類的作用和Proxy類的作用相同 (這里關(guān)鍵是如何通過(guò)該方法動(dòng)態(tài)產(chǎn)生代理,以支持實(shí)現(xiàn)了某些接口的任意類任意方法產(chǎn)生代理)。
動(dòng)態(tài)代理實(shí)現(xiàn)思路:
【a】聲明一段源碼:該源碼就是在Proxy類中定義一個(gè)字符串,該字符串的值就是靜態(tài)代理TimeProxy類的源碼(這里在它的基礎(chǔ)上進(jìn)行改造)。
手動(dòng)創(chuàng)建Proxy類,并提供newInstance的靜態(tài)方法(模擬JDK動(dòng)態(tài)代理的Proxy調(diào)用靜態(tài)方法newInstance()),并在該方法中聲明一個(gè)字符串該字符串的值是靜態(tài)代理類TimeProxy(源碼)的所有,如下圖(Windows下的換行符:"\r\n"。)。
這里給代理類的文件名換成JDK生成動(dòng)態(tài)代理的文件名$Proxy0。
注意:由于源碼中已經(jīng)有引號(hào)了,所以可以通過(guò)轉(zhuǎn)義引號(hào)\"\"來(lái)代替引號(hào)。
通過(guò)JDK動(dòng)態(tài)產(chǎn)生代理類獲取名字方式:因?yàn)樵摲绞椒祷氐木褪谴眍?,代理?duì)象.getClass.getName(),結(jié)果可以看到JDK產(chǎn)生動(dòng)態(tài)代理的名字就是$Proxy0。
【b】編譯源碼(編譯時(shí)使用JDK Compiler API),編譯成功后就會(huì)產(chǎn)生新的類(該類就是代理類)
編譯的方式:一般先生成一個(gè)java文件,然后對(duì)該java文件進(jìn)行編譯。
首先定義文件的路徑,文件路徑的值一般取當(dāng)前應(yīng)用所在的路徑(當(dāng)前路徑獲取方式:System.getProperty("user.dir"),這樣方便進(jìn)行編譯。如下圖,測(cè)試輸出。也可以把該項(xiàng)目放在指定目錄下,在后面拼接指定路徑加類名。
根據(jù)這個(gè)路徑生成一個(gè)java文件。
接下來(lái)需要把該源碼生成到j(luò)ava文件當(dāng)中,通過(guò)commons-io包下的FileUtils類可以快速的對(duì)文件進(jìn)行讀寫刪除等等操作,這里使用writeStringToFile(File對(duì)象,Stirng對(duì)象)(jre1.6不支持編譯,這里改成jdk)
這樣在運(yùn)行測(cè)試程序時(shí),就會(huì)生成一個(gè)$Proxy0.java文件放到指定目錄下,可以在Navigator視圖下的bin目錄下查看到。
因?yàn)閷?shí)現(xiàn)的目標(biāo)是想對(duì)任意對(duì)象任意方法產(chǎn)生任意的代理,所以該方法還需要改造,也就是源碼中代理類。
由于是要?jiǎng)討B(tài)產(chǎn)生代理,所以靜態(tài)代理源碼中實(shí)現(xiàn)接口的方法也需要通過(guò)傳進(jìn)來(lái)的參數(shù)來(lái)獲取。所以定義一個(gè)String用來(lái)獲取方法。
還需要把方法替換成methodStr,如圖
測(cè)試:
查看全部 -
使用cglib動(dòng)態(tài)產(chǎn)生代理
JDK動(dòng)態(tài)代理機(jī)制:只能代理實(shí)現(xiàn)某些接口的類,如果沒有實(shí)現(xiàn)接口的類則不能使用JDK動(dòng)態(tài)代理。
cglib動(dòng)態(tài)代理機(jī)制:針對(duì)類產(chǎn)生代理,原理就是為指定的目標(biāo)類產(chǎn)生一個(gè)子類,子類通過(guò)方法攔截技術(shù)(覆蓋父類的方法來(lái)實(shí)現(xiàn)功能的增強(qiáng))攔截所有父類方法的調(diào)用(因?yàn)樵摲N方式是使用繼承方式,所以不能對(duì)final修飾的類進(jìn)行代理)。
CGLIB產(chǎn)生代理案例:
步驟1:導(dǎo)入cglib的jar包。
步驟2:創(chuàng)建一個(gè)Train類,它并沒有實(shí)現(xiàn)某個(gè)接口,并提供給它一個(gè)move方法。
步驟3:創(chuàng)建一個(gè)CglibProxy類來(lái)產(chǎn)生代理,它需要實(shí)現(xiàn)接口MethodInterceptor,并實(shí)現(xiàn)了intercept方法,該方法作用就是攔截所有目標(biāo)類方法的調(diào)用(第一個(gè)參數(shù):目標(biāo)類的實(shí)例。第二個(gè)參數(shù):目標(biāo)方法的反射對(duì)象。第三個(gè)參數(shù):目標(biāo)方法的參數(shù)。第四個(gè)參數(shù):Proxy代理類的實(shí)例)。
調(diào)用被代理類的方法:在intercept方法中,通過(guò)代理類實(shí)例的invokeSuper(obj,m)方法調(diào)用父類的方法,第一個(gè)參數(shù)就是目標(biāo)類的對(duì)象。第二個(gè)參數(shù)是目標(biāo)類方法的參數(shù)。(因?yàn)閏glib是使用繼承的方式實(shí)現(xiàn)動(dòng)態(tài)代理,所以產(chǎn)生的代理類是被代理類的子類)
該方法需要返回該代理
步驟5:由于調(diào)用intercept方法中需要傳入代理類實(shí)例,所以在該類中需要提供得到代理類實(shí)例的方法,創(chuàng)建代理類需要使用Enhancer屬性,而且通過(guò)Enhancer創(chuàng)建代理類還需要傳入被代理類的類類型,enhancer.setSuperclass(被代理類的類類型),表示要?jiǎng)?chuàng)建哪一個(gè)類的代理,在創(chuàng)建代理實(shí)例之前,還需要調(diào)用回調(diào)函數(shù)enhancer.setCallback(this),最后返回代理實(shí)例enhancer.create();
測(cè)試:首先需要?jiǎng)?chuàng)建擁有返回代理類實(shí)例方法的對(duì)象,通過(guò)這個(gè)對(duì)象調(diào)用返回代理實(shí)例的方法,然后賦值給該類,再調(diào)用方法。
查看全部 -
問(wèn)題:這里是Car需要代理來(lái)實(shí)現(xiàn)時(shí)間和日志的處理,如果有一個(gè)火車,那么還需要增加一個(gè)火車時(shí)間處理的代理類。不同的類實(shí)現(xiàn)這個(gè)代理能不能組合成一個(gè)代理呢?
動(dòng)態(tài)代理概念:動(dòng)態(tài)產(chǎn)生代理,實(shí)現(xiàn)對(duì)不同類,不同方法的代理。
JDK動(dòng)態(tài)代理實(shí)現(xiàn)方式:在ProxySubject代理類和RealSubject被代理類之間增加了一個(gè)實(shí)現(xiàn)了InvocationHandler的一個(gè)ProxyHandler類(事物處理器),日志處理和時(shí)間處理都是在事物處理中完成的。
其中Java動(dòng)態(tài)代理類(InvocationHandler)位于位于java.lang.reflect包下,一般主要涉及到以下兩個(gè)類:
(1)Interface InvocationHandler:該接口中僅定義了一個(gè)方法public object invoke(Object obj,Method method,Object[] args)在實(shí)際使用時(shí),第一個(gè)參數(shù)表示被代理的對(duì)象,第二個(gè)參數(shù)代表被代理的方法,第三個(gè)參數(shù)代表被代理方法的參數(shù),args是一個(gè)數(shù)組。這個(gè)抽象方法在代理類中動(dòng)態(tài)實(shí)現(xiàn)。
(2)Proxy:產(chǎn)生動(dòng)態(tài)代理的類,通過(guò)static Object newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h)就可以動(dòng)態(tài)返回代理類的一個(gè)實(shí)例,返回后的代理類可以當(dāng)做被代理類使用(可使用被代理類的在接口中聲明過(guò)的方法)。第一個(gè)參數(shù)是被代理類的類加載器。第二個(gè)參數(shù)是被代理類實(shí)現(xiàn)了哪些接口。第三個(gè)參數(shù)是事件處理器。
代碼實(shí)現(xiàn):
【步驟1】由于要使用Proxy類動(dòng)態(tài)生成代理的方法中第三個(gè)參數(shù)需要傳入事物處理器的對(duì)象。首先需要?jiǎng)?chuàng)建事物處理器。如下圖,由于invoke方法中使用到被代理對(duì)象,所以構(gòu)造方法中傳入被代理對(duì)象的實(shí)例,invoke方法中就是為了實(shí)現(xiàn)代理功能,通過(guò)method.invoke(被代理的對(duì)象),這樣被代理對(duì)象的方法就會(huì)執(zhí)行了。
【步驟2】通過(guò)產(chǎn)生動(dòng)態(tài)代理的Proxy類的newProxyInstance方法可以產(chǎn)生代理,第一個(gè)參數(shù)為被代理對(duì)象的類加載器,第二個(gè)參數(shù)為被代理對(duì)象實(shí)現(xiàn)的接口,第三個(gè)參數(shù)為事物處理器。
總結(jié):
JDK動(dòng)態(tài)代理是被代理類首先需要實(shí)現(xiàn)某些接口,這個(gè)代理在運(yùn)行時(shí)產(chǎn)生代理對(duì)象,但該代理并不能做任何操作,操作是通過(guò)實(shí)現(xiàn)InvocationHandler接口的事物處理器實(shí)現(xiàn)的。
查看全部 -
聚合比繼承更適合代理模式
案例描述:上節(jié)課程中,對(duì)Car類的move方法增加了時(shí)間處理的額外功能,現(xiàn)在想實(shí)現(xiàn)代理之間功能的疊加,也就是在move方法前后不僅做時(shí)間的處理,還做權(quán)限的管理和增加日志的管理。
通過(guò)繼承實(shí)現(xiàn)上述功能:Car1的move方法里調(diào)用Car的move方法前后增加了時(shí)間處理的功能,現(xiàn)在要做代理類功能的疊加(先記錄日志再記錄汽車行駛的時(shí)間)。首先需要?jiǎng)?chuàng)建一個(gè)Car3,來(lái)繼承Car1(Car1通過(guò)繼承方式實(shí)現(xiàn)靜態(tài)代理),或者繼承Car,然后在move方法前后先實(shí)現(xiàn)日志,再實(shí)現(xiàn)汽車行駛時(shí)間的處理(這樣會(huì)先輸出日志再輸出時(shí)間處理)。
如果先要記錄汽車行駛的時(shí)間,再進(jìn)行日志的處理,則需要?jiǎng)?chuàng)建一個(gè)Car4,先對(duì)時(shí)間處理,再對(duì)日志處理。
如果先要權(quán)限的判斷、再進(jìn)行日志的處理、然后再進(jìn)行時(shí)間處理,則需要?jiǎng)?chuàng)建一個(gè)Car5。
如果先要權(quán)限的判斷、再進(jìn)行時(shí)間的處理、然后再進(jìn)行日志的處理,則需要?jiǎng)?chuàng)建一個(gè)Car6.
結(jié)果得出如果使用繼承的方式實(shí)現(xiàn)代理功能的疊加,代理類會(huì)無(wú)限的增多,也就是用一個(gè)代理則需要?jiǎng)?chuàng)建一個(gè)代理,所以繼承的方式實(shí)現(xiàn)代理功能的疊加不是推薦的。
通過(guò)聚合實(shí)現(xiàn)上述功能:由于代理類和被代理類都實(shí)現(xiàn)了相同的接口 ,所以代理類的構(gòu)造方法中也可以傳入接口的對(duì)象(針對(duì)Car2代理類),代理類中聲明的也是接口的對(duì)象。再創(chuàng)建一個(gè)CarLogProxy代理類(它實(shí)現(xiàn)了接口)用來(lái)做日志的功能。
如下是先記錄日志再記錄時(shí)間
如下是先記錄時(shí)間再記錄日志
結(jié)果通過(guò)聚合方式實(shí)現(xiàn)代理會(huì)比繼承方式靈活很多,使用聚合方式代理之間是可以相互傳遞的,所以推薦使用聚合方式實(shí)現(xiàn)代理模式(這里通過(guò)聚合實(shí)現(xiàn)代理實(shí)現(xiàn)了不同功能的順序)。
查看全部 -
以智能引用代理為例:
代理的兩種實(shí)現(xiàn)方式
案例描述:提供一個(gè)接口,該接口中提供一個(gè)車的move方法。正常情況下沒有使用代理時(shí),創(chuàng)建一個(gè)Car實(shí)現(xiàn)該接口,并實(shí)現(xiàn)該接口的方法,然后就可以通過(guò)創(chuàng)建該實(shí)現(xiàn)類的對(duì)象并賦值給接口來(lái)調(diào)用該方法了。
【1】靜態(tài)代理:代理和被代理對(duì)象在代理之前是確定的,他們都實(shí)現(xiàn)相同的接口或者繼承相同的抽象類。
【a】繼承的方式實(shí)現(xiàn)靜態(tài)代理:正常情況下是通過(guò)Car的方法來(lái)完成操作,代理的意思是,創(chuàng)建一個(gè)類Car1并繼承類Car,并在繼承類Car1的構(gòu)造方法中通過(guò)super調(diào)用父類的方法,也可以在方法中添加一些額外的功能,這個(gè)類Car1就是類Car的代理,最后就可以通過(guò)創(chuàng)建Car1的對(duì)象賦值給接口,再調(diào)用move方法。
【b】聚合的方式實(shí)現(xiàn)靜態(tài)代理:聚合就是在該類中聲明被代理的對(duì)象,創(chuàng)建一個(gè)Car2,并實(shí)現(xiàn)該接口,然后在類中聲明Car,通過(guò)構(gòu)造方法對(duì)Car的對(duì)象進(jìn)行賦值,然后在實(shí)現(xiàn)的方法中通過(guò)Car對(duì)象調(diào)用move()方法。
【2】動(dòng)態(tài)代理
查看全部 -
代理模式概念:為其他對(duì)象提供一種代理以控制對(duì)這個(gè)對(duì)象的訪問(wèn),代理對(duì)象起到中介作用。
常見的幾種代理模式:
1、遠(yuǎn)程代理:代理模式的經(jīng)典應(yīng)用,類似于客戶端、服務(wù)器的模式,為不同地理的對(duì)象提供局域網(wǎng)代表對(duì)象。
2、保護(hù)代理:控制對(duì)一個(gè)對(duì)象的訪問(wèn)權(quán)限。
3、智能代理:提供目標(biāo)對(duì)象額外的一些服務(wù)。
4、虛擬代理:根據(jù)需要將資源消耗很大的對(duì)象進(jìn)行延遲,真正需要的時(shí)候進(jìn)行創(chuàng)建。
查看全部
舉報(bào)