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