Proxy 代理模式 動態代理 CGLIB

代理的基本概念

幾個英文單詞:
proxy [ˈprɒksi] n.  代理服務器;表明權;代理人,代替物;委託書;
invoke [ɪnˈvəʊk] vt. 乞靈,祈求;提出或授引…以支持或證實;召鬼;藉助;
invocation [ˌɪnvəˈkeɪʃn] n.  祈禱;乞求;乞靈;乞求神助;
subject	[ˈsʌbdʒɪkt] n. 主題,話題;學科,科目;[哲] 主觀; 
    adj. 須服從…的;(在君主等)統治下的; 
    v. 提供,提出;使…隸屬;

什麼是代理
咱們你們都知道微商代理,簡單地說就是代替廠家賣商品,廠家"委託"代理爲其銷售商品。關於微商代理,首先咱們從他們那裏買東西時一般不知道背後的廠家到底是誰(不要鑽牛角尖!),也就是說,"委託者"對咱們來講是不可見的;其次,微商代理主要以朋友圈的人爲目標客戶,這就至關於爲廠家作了一次對客戶羣體的"過濾"。
咱們把微商代理和廠家進一步抽象,前者可抽象爲"代理類",後者可抽象爲"委託類(被代理類)"。

經過使用代理,一般有兩個優勢:
  • 能夠隱藏委託類的實現
  • 能夠實現客戶與委託類間的解耦,在不修改委託類代碼的狀況下可以作一些額外的處理。

代理模式是一種結構型設計模式,其目的就是 爲其餘對象提供一個代理以控制對某個對象的訪問。代理類負責爲委託類 預處理消息,過濾消息並轉發消息,以及進行消息被委託類執行後的後續處理

其實方法直接調用就能夠完成功能,爲何還要加個代理呢?
緣由是 採用代理模式能夠有效的將具體的實現與調用方進行解耦,經過面向接口進行編程徹底將具體的實現隱藏在內部
更通俗的說,代理解決的問題是:當兩個類須要通訊時,引入第三方代理類,將兩個類的關係解耦,讓咱們只瞭解代理類便可,並且代理的出現還可讓咱們完成與另外一個類之間的關係的統一管理。可是一個前提, 代理類和委託類要實現相同的接口,由於代理真正調用的仍是委託類的方法。

按照代理的建立時期,能夠分爲兩種:

  • 靜態代理:若代理類在程序運行前就已經存在,那麼這種代理方式被成爲靜態代理 。靜態代理一般是由程序員在Java代碼中定義的, 且代理類和委託類會實現同一接口或是派生自相同的父類。
  • 動態代理:在程序運行時運用反射機制動態建立而成。

靜態代理

靜態代理的通常實現過程:
  • 首先建立一個接口
  • 而後建立具體實現類來實現這個接口具體實現類中須要將接口中定義的方法的業務邏輯功能實現
  • 再建立一個代理類一樣實現這個接口代理類中接口方法只要調用具體類中的對應方法便可
這樣,咱們在須要使用接口中的某個方法時,直接調用代理類的方法便可,而具體的實現隱藏在了底層。
/**第一步:定義一個接口*/
interface Iuser {
	void eat(String s);
}

/**第二步:建立具體實現類,即被代理類或委託類*/
class UserImpl implements Iuser {
	@Override
	public void eat(String s) {
		System.out.println("我要吃" + s);
	}
}

/**第三步:建立代理類*/
class UserProxy implements Iuser {
	private Iuser user;//代理類一般只實現一個接口,由於代理類中持有的一般只是接口的引用,而不是某個委託類的直接引用

	public UserProxy() {
		this.user = new UserImpl();
	}

	public UserProxy(Iuser user) {
		this.user = user;
	}

	@Override
	public void eat(String s) {
		System.out.println("靜態代理前置內容");
		user.eat(s);//利用的就是多態的特性,也是面向接口編程的一種典型的體現
		System.out.println("靜態代理後置內容");
	}
}

/**靜態代理訪問演示*/
public class Test {
	public static void main(String[] args) {
		UserProxy proxy = new UserProxy();//或 UserProxy proxy = new UserProxy(new UserImpl())
		proxy.eat("蘋果");
	}
}
代理類和被代理類 都須要實現某個功能接口, 代理類 裏面持有被 代理類 的引用, 代理類 能夠根據須要添加不一樣的操做。

靜態代理類的 優勢: 客戶端不須要知道實現類是什麼、怎麼作的,而只需知道代理類便可(不要鑽牛角尖)。
靜態代理類的缺點:
  • 代理類和委託類實現了相同的接口,代理類經過委託類實現了相同的方法,可是由於代理類中接口的方法每每是沒什麼邏輯的,它一般只是調用了委託類的同名方法而已,因此這就出現了大量重複、冗餘的代碼
  • 若是接口中增長或修改了某個方法,除了全部委託類須要修改代碼外,全部代理類也須要修改代碼,增長了代碼維護的複雜度
  • 代理對象只服務於一種類型的對象即靜態代理類只能爲特定的接口服務,如想要爲多個接口服務則須要創建多個代理類,這在大型系統中大大增長了複雜度。

動態代理

靜態代理是在編譯時就將接口、實現類、代理類一古腦兒所有手動完成 ,但若是咱們須要不少的代理,每個都這麼手動的去建立實屬浪費時間,並且會有大量的重複代碼,此時咱們就能夠採用動態代理。

動態代理能夠在程序運行期間根據須要動態的建立代理類及其實例,來完成具體的功能,主要用的是JAVA的反射機制
也就是說,這 種狀況下,代理類並非在Java代碼中定義的,而是在運行時根據咱們在Java代碼中的"指示"動態生成的。
/**第一步:定義一個接口*/
interface Iuser {
	void eat(String s);
}

/**再定義一個接口*/
interface Irun {
	String run(int length);
}

/**第二步:建立具體實現類,即被代理類或委託類*/
class UserImpl implements Iuser, Irun {
	@Override
	public void eat(String s) {
		System.out.println("我要吃" + s);
	}

	@Override
	public String run(int length) {
		System.out.println("我跑了 " + length + " 米");
		return "跑步很歡樂";
	}
}

/**第三步:定義代理實例的【調用處理器】,這是一個位於代理類與委託類之間的【中介類】,它須要實現【InvocationHandler】接口*/
class UserHandler implements InvocationHandler {
	private Object object;//調用處理器持有委託類的引用,但並不限定委託類必須是某一接口或某一類

	public UserHandler(Object object) {//這裏是用Object接收的,因此能夠傳遞任何類型的對象
		this.object = object;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		Object returnObj = method.invoke(object, args); //【核心點】經過反射執行某個類的某方法
		System.out.println("【方法】" + method.getName() + "【參數】" + Arrays.toString(args) + "【返回值】" + returnObj);
		return returnObj;
	}
}

/**第四步:在使用時動態建立動態代理類*/
public class Test {
	public static void main(String[] args) {
		test1();
		System.out.println("----------------------2---------------------");
		test2();
		System.out.println("----------------------3---------------------");
		test3();
	}

	private static void test1() {//徹底基於接口Iuser的用法
		Iuser user = new UserImpl();
		System.out.println("【委託類】" + user.getClass().getName());//UserImpl
		System.out.println("【委託類實現的接口】" + Arrays.toString(user.getClass().getInterfaces()));//[interface Iuser, interface Irun]

		InvocationHandler handler = new UserHandler(user);
		Iuser proxy = (Iuser) Proxy.newProxyInstance(Iuser.class.getClassLoader(), new Class[] { Iuser.class }, handler);//能夠直接強轉爲Iuser
		System.out.println("【代理類實現的接口】" + Arrays.toString(proxy.getClass().getInterfaces()));//[interface Iuser]。由於你只指定了Iuser接口
		proxy.eat("蘋果");
	}

	private static void test2() {//精簡形式
		Iuser proxy = (Iuser) Proxy.newProxyInstance(Iuser.class.getClassLoader(), new Class[] { Iuser.class }, new UserHandler(new UserImpl()));
		System.out.println("【代理類實現的接口】" + Arrays.toString(proxy.getClass().getInterfaces()));//[interface Iuser, interface Irun]
		proxy.eat("香蕉你個巴拉");
	}

	private static void test3() {//徹底基於委託類UserImpl,之因此採用這種方式,是由於委託類實現了多個接口,且咱們須要調用不一樣接口中的方法
		Object proxy = Proxy.newProxyInstance(UserImpl.class.getClassLoader(), UserImpl.class.getInterfaces(), new UserHandler(new UserImpl()));
		((Iuser) proxy).eat("你妹");//能夠強轉爲Iuser
		String returnObj = ((Irun) proxy).run(99);//也能夠強轉爲Irun
		System.out.println(returnObj);
	}
}
動態代理的實現其實與靜態代理相似,都須要建立代理類,可是不一樣之處也很明顯,建立方式不一樣! 不一樣之處體如今 靜態代理咱們知根知底,咱們知道要對哪一個接口、哪一個實現類來建立代理類,因此咱們在編譯前就直接實現與實現類相同的接口,直接在實現的方法中調用實現類中的相應同名方法便可 ;而動態代理不一樣, 咱們不知道它何時建立,也不知道要建立針對哪一個接口、實現類的代理類 (由於它是在運行時因需實時建立的)。


動態代理用到的兩個類
proxy instance:代理實例,即代理類的實例,指得是經過 Proxy.newProxyInstance 產生的對象
the invocation handler of a proxy instance:代理實例的調用處理器,指的是實現InvocationHandler接口的那個中介類

接口 InvocationHandler 調用處理器

java.lang.reflect.InvocationHandler
InvocationHandler is the interface implemented by the invocation handler of a proxy instance.
InvocationHandler是代理實例調用處理器實現的接口。
PS:InvocationHandler的實現類並非代理類,而只是位於代理類與委託類之間的中介類。

Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler.
每一個 代理實例都具備一個 關聯的調用處理器。對代理實例 調用方法時,將對 方法調用進行編碼,並將其指派到,它的調用處理器的 invoke 方法。
換一種語氣描述就是:每一個代理類的實例都關聯到了一個實現InvocationHandler接口的handler,當咱們經過代理對象調用一個方法的時候,這個方法的調用就會被轉發爲由InvocationHandler這個接口的 invoke 方法來進行調用。

惟一的一個方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; //在代理實例上處理方法調用並返回結果。
在代理實例上處理方法調用並返回結果。在與方法關聯的代理實例上調用方法時,將在調用處理程序上調用此方法。
參數:
  • proxy - 在其上調用方法的代理實例
  • method - 對應於在代理實例上調用的接口方法的 Method 實例。 Method 對象的聲明類將是在其中聲明方法的接口,該接口能夠是代理類賴以繼承方法的代理接口的超接口。
  • args - 包含傳入代理實例上方法調用的參數值的對象數組,若是接口方法不使用參數,則爲 null。基本類型的參數被包裝在適當基本包裝器類的實例中。
返回:
  • 從代理實例的方法調用返回的值。
  • 若是接口方法的聲明返回類型是基本類型,則此方法返回的值必定是相應基本包裝對象類的實例;不然,它必定是可分配到聲明返回類型的類型。
  • 若是此方法返回的值爲 null 而且接口方法的返回類型是基本類型,則代理實例上的方法調用將拋出 NullPointerException。不然,若是此方法返回的值與上述接口方法的聲明返回類型不兼容,則代理實例上的方法調用將拋出 ClassCastException。

Proxy 動態代理類

public class java.lang.reflect.Proxy extends Object implements java.io.Serializable
Proxy 提供用於建立動態代理類和實例的靜態方法, 它仍是由這些方法建立的全部動態代理類的超類

基本簡介

建立某一接口 Foo 的代理:
InvocationHandler handler = new MyInvocationHandler(new FooImpl());//FooImpl是實現 Foo 接口的某一委託類
Foo f = (Foo) Proxy.getProxyClass(Foo.class.getClassLoader(), new Class[] { Foo.class })
    .getConstructor(new Class[] { InvocationHandler.class })
    .newInstance(new Object[] { handler });
或使用如下更簡單的方法:
Foo proxy = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),//定義了由哪一個ClassLoader對象來對生成的代理對象進行加載
		new Class[] { Foo.class },//表示的是我將要給我須要代理的對象提供一組什麼接口,以後我這個代理對象就會實現了這組接口
		handler);//表示的是當我這個動態代理對象在調用方法的時候,會關聯到哪個InvocationHandler對象上

動態代理類(如下簡稱爲代理類)是一個實如今建立類時 在運行時指定的接口列表的類,該類具備下面描述的行爲。 代理接口 是代理類實現的一個接口。 代理實例 是代理類的一個實例。 每一個代理實例都有一個關聯的 調用處理程序 對象,它能夠實現接口InvocationHandler。經過其中一個代理接口的代理實例上的方法調用將被指派到實例的調用處理程序的 Invoke 方法,並傳遞代理實例、識別調用方法的 java.lang.reflect.Method 對象以及包含參數的 Object 類型的數組。調用處理程序以適當的方式處理編碼的方法調用,而且它返回的結果將做爲代理實例上方法調用的結果返回。

代理類具備的屬性(properties)

A proxy class has the following properties:
  • 代理類是公共的、最終的,而不是抽象的。Proxy classes are public, final, and not abstract.
//這句話我以爲是有問題的,以下:
System.out.println("【代理類的修飾符】" + Modifier.toString(proxy.getClass().getModifiers()));//final,並非public的
  • 未指定代理類的非限定名稱 The unqualified name of a proxy class is unspecified。可是,以字符串 "$Proxy" 開頭的類名空間應該爲代理類保留
System.out.println("【代理類】" + proxy.getClass().getName());//【$Proxy0】
  • 代理類的超類爲(A proxy class extends) java.lang.reflect.Proxy
System.out.println("【代理類的超類】" + proxy.getClass().getSuperclass());//【class java.lang.reflect.Proxy】
  • 代理類會按同一順序準確地實現其建立時指定的接口
  • 若是代理類實現了非公共接口,那麼它將在與該接口相同的包中定義。If a proxy class implements a non-public interface, then it will be defined in the same package as that interface. 不然,代理類的包也是未指定的 unspecified。注意,包密封 package sealing 將不阻止代理類在運行時在特定包中的成功定義,也不會阻止相同類加載器和帶有特定簽名的包所定義的類。
  • 因爲代理類將實現全部在其建立時指定的接口,因此對其 Class 對象調用 getInterfaces 將返回一個包含相同接口列表的數組(按其建立時指定的順序),對其 Class 對象調用 getMethods 將返回一個包括這些接口中全部方法的 Method 對象的數組,而且調用 getMethod 將會在代理接口中找到指望的 as would be expected 一些方法。
System.out.println("【代理類實現的接口】" + Arrays.toString(proxy.getClass().getInterfaces()));//[interface Iuser, interface Irun]
  • 若是 Proxy.isProxyClass 方法傳遞代理類(由 Proxy.getProxyClass 返回的類,或由 Proxy.newProxyInstance 返回的對象的類),則該方法返回 true,不然返回 false。
System.out.println("【是不是代理類】" +Proxy.isProxyClass(proxy.getClass()));//true
  • 代理類的 java.security.ProtectionDomain 與由引導類加載器(如 java.lang.Object)加載的系統類相同,緣由是代理類的代碼由受信任的系統代碼生成。此保護域一般被授予 java.security.AllPermission。
  • 每一個代理類都有一個能夠帶一個參數(接口 InvocationHandler 的實現)的公共構造方法,用於設置代理實例的調用處理程序。並不是必須使用反射 API 才能訪問公共構造方法,經過調用 Proxy.newInstance 方法(將調用 Proxy.getProxyClass 的操做和調用帶有調用處理程序的構造方法結合在一塊兒)也能夠建立代理實例。

代理實例具備的屬性

  • 提供代理實例 proxy 和一個由其代理類 Foo 實現的接口,表達式 proxy instanceof Foo 將返回 true,而且 (Foo) proxy 的強制轉換操做將會成功(而不拋出 ClassCastException):
//指定代理類實現的接口爲:new Class[] { Iuser.class, Irun.class }
((Iuser) proxy).eat("蘋果");//能夠強轉爲Iuser
((Irun) proxy).run(99);//也能夠強轉爲Irun
  • 每一個代理實例都有一個關聯的調用處理程序,它會被傳遞到其構造方法中。靜態 Proxy.getInvocationHandler 方法將返回與做爲其參數傳遞的代理實例相關的調用處理程序。
Iuser proxy = (Iuser) Proxy.newProxyInstance(Iuser.class.getClassLoader(), new Class[] { Iuser.class }, handler);
System.out.println(Proxy.getInvocationHandler(proxy) == handler);//true
  • 代理實例上的接口方法調用將按照該方法的文檔描述進行編碼,並被指派到調用處理程序的 Invoke 方法。
  • 在代理實例上的 java.lang.Object 中聲明的 hashCode、equals 或 toString 方法的調用將按照與編碼和指派接口方法調用相同的方式進行編碼,並被指派到調用處理程序的 invoke 方法,如上所述。傳遞到 invoke 的 Method 對象的聲明類是 java.lang.Object。代理類不重寫從 java.lang.Object 繼承的代理實例的其餘公共方法,因此這些方法的調用行爲與其對 java.lang.Object 實例的操做同樣。

在多代理接口中重複的方法

Methods Duplicated in Multiple Proxy Interfaces
當代理類的 兩個或多個接口包含一個 具備相同名稱和參數簽名的方法時,代理類的接口順序變得很是重要。在代理實例上調用重複方法時,傳遞到調用處理程序的 Method 對象沒有必要成爲其聲明類能夠從接口(經過該接口調用代理方法)的引用類型指派的對象。此限制存在的緣由是, 生成的代理類中的相應方法實現沒法肯定它經過哪個接口調用。所以,在代理實例上調用重複方法時,第一個接口中的方法的 Method 對象包含接口的代理類列表中的方法(直接或經過超級接口繼承),該對象會傳遞到調用處理程序的 invoke 方法,不管該方法調用經過哪種引用類型發生。

若是代理接口包含某一方法,它的名稱和參數簽名與 java.lang.Object 的 hashCode、equals 或 toString 方法相同,那麼在代理實例上調用這樣的方法時,傳遞到調用處理程序的 Method 對象將使 java.lang.Object 成爲其聲明類。換句話說,java.lang.Object 公共的非最終方法理論上在全部代理接口以前,以便肯定哪個 Method 對象傳遞到調用處理程序。

還要注意,當重複方法被指派到調用處理程序時,invoke 方法只能夠拋出通過檢查的異常類型,該異常類型可使用全部 代理接口(能夠經過它調用)中方法的 throws 子句指派一種異常類型。若是 invoke 方法拋出一個通過檢查的異常,該異常沒有指派給任何由一個代理接口(能夠經過它調用)中的方法聲明的異常類型,那麼該代理實例上的調用將拋出一個未經檢查的 UndeclaredThrowableException。此限制表示並不是全部的由傳遞到 invoke 方法的 Method 對象上調用 getExceptionTypes 返回的異常類型均可以由 invoke 方法成功拋出。

API

字段
  • protected InvocationHandler  h  此代理實例的調用處理程序。
構造方法
  • protected Proxy(InvocationHandler h)  使用其調用處理程序的指定值從子類(一般爲動態代理類)構建新的 Proxy 實例。
公共方法
  • static InvocationHandler    getInvocationHandler(Object proxy)  返回指定代理實例的調用處理程序。
  • static boolean    isProxyClass(Class<?> cl)  當且僅當指定的類經過 getProxyClass 方法或 newProxyInstance 方法動態生成爲代理類時,返回 true。
  • static Class<?>    getProxyClass(ClassLoader loader, Class<?>... interfaces)  返回代理類的 java.lang.Class 對象,並向其提供類加載器和接口數組。
  • static Object    newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)  返回一個指定接口的代理類實例,該接口能夠將方法調用指派到指定的調用處理程序。
    • 返回值:一個帶有代理類的指定調用處理程序的代理實例,它由指定的類加載器定義,並實現指定的接口
    • 參數:
      • loader - 定義代理類的類加載器。定義了由哪一個ClassLoader對象來對生成的代理對象進行加載
      • interfaces - 代理類要實現的接口列表。表示的是我將要給我須要代理的對象提供一組什麼接口,以後我這個代理對象就會實現了這組接口
      • h - 指派方法調用的調用處理程序。表示的是當我這個動態代理對象在調用方法的時候,會關聯到哪個InvocationHandler對象上

關於 newProxyInstance 方法

爲何咱們能夠將newProxyInstance方法返回的Object類型的對象轉化爲Iuser或Irun類型的對象?
緣由就在newProxyInstance這個方法的第二個參數上,咱們給這個代理對象提供了一組什麼接口,那麼我這個代理對象就會實現這組接口,這個時候咱們固然能夠將這個代理對象強制類型轉化爲這個接口的對象。
另外,經過 Proxy.newProxyInstance 建立的代理對象是在jvm運行時動態生成的一個對象,它實際的類型並非咱們的 InvocationHandler 類型,也不是咱們定義的那組接口的類型,而是在運行時動態生成的一個對象,而且命名方式都是這樣的形式,以$開頭,Proxy爲中,最後一個數字表示對象的標號。

咱們來看看這兩句:
((Iuser) proxy).eat("蘋果");
((Irun) proxy).run(99);
這裏是經過代理對象來調用實現的那種接口中的方法,這個時候程序就會跳轉到由這個代理對象關聯到的 handler 中的 invoke 方法去執行,而咱們的這個 handler 對象又接受了一個 UserImpl 類型的參數,表示我要代理的就是這個真實對象,因此此時就會調用 handler 中的 invoke 方法去執行。

CGLIB代碼生成庫簡介

Byte Code Generation Library is high level API to generate and transform JAVA byte code. It is used by AOP, testing, data access frameworks to generate dynamic proxy objects and intercept field access. 
字節碼生成庫是用於生成和轉換JAVA字節碼的高級API。它被AOP,測試,數據訪問框架用於生成動態代理對象並攔截字段訪問。

JDK從1.3版本起就提供了一個動態代理,它使用起來很是簡單,可是有個明顯的缺點: 須要目標對象實現一個或多個接口
一般來講,你可使用JDK動態代理方法來建立代理, 對於沒有接口的狀況或者性能因素 ,CGLIB是一個很好的選擇。
使用CGLIB即便代理類沒有實現任何接口也能夠實現動態代理功能。 CGLIB簡單易用,且它的運行速度要遠遠快於JDK的Proxy動態代理。 本質上來講,CGLIB經過產生子類覆蓋 非final 方法來進行代理,CGLIB不能代理一個final類或者final方法。

CGLIB是一個強大的、高性能的代碼生成庫。它被普遍使用在 基於代理的AOP框架(例如Spring AOP和dynaop)提供方法攔截。Hibernate做爲最流行的ORM工具也一樣使用CGLIB庫來代理單端關聯(集合懶加載除外,它使用另一種機制)。EasyMock和jMock做爲流行的Java測試庫,它們提供Mock對象的方式來支持測試,都使用了CGLIB來對沒有接口的類進行代理。

在實現內部,CGLIB庫使用了 ASM這一個輕量但高性能的字節碼操做框架來轉化字節碼,產生新類。除了CGLIB,像Groovy和BeanShell這樣的腳本語言一樣使用ASM來生成Java字節碼。ASM使用了一個相似於SAX分析器的機制來達到高性能。咱們不建議直接使用ASM,由於這樣須要對JVM很是瞭解,包括類文件格式和指令集。


CGLIB API
CGLIB庫組織以下所示:
  • net.sf.cglib.core:底層字節碼操做類;大部分與ASP相關。
  • net.sf.cglib.transform:編譯期、運行期的class文件轉換類。
  • net.sf.cglib.proxy:代理建立類、方法攔截類
  • net.sf.cglib.reflect:更快的反射類、C#風格的代理類。
  • net.sf.cglib.util:集合排序工具類
  • net.sf.cglib.beans:JavaBean相關的工具類
對於建立動態代理,大部分狀況下你只須要使用proxy包的一部分API便可。


一個案例
import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

interface LoginService {
	public boolean checkUser();
}

class LoginServiceImpl implements LoginService {
	@Override
	public boolean checkUser() {
		System.out.println("LoginServiceImpl  checkUser");
		return false;
	}
}

interface UserService {
	public String getUserName();
}

class UserServiceImpl implements UserService {

	@Override
	public String getUserName() {
		System.out.println("UserServiceImpl getUserName");
		return null;
	}

}

class CglibProxy implements MethodInterceptor {
	@Override
	public Object intercept(Object proxy, Method method, Object[] params, MethodProxy methodProxy) throws Throwable {
		System.out.println("*********代理方法執行前************");
		Object retObj = methodProxy.invokeSuper(proxy, params);
		System.out.println("*********代理方法執行後************");
		return retObj;
	}

	//返回目標對象的代理對象  
	public Object newProxy(Object target) {
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(target.getClass());
		enhancer.setCallback(this);
		enhancer.setClassLoader(target.getClass().getClassLoader());
		return enhancer.create();
	}
}

public class Test {

	public static void main(String[] args) {
		//建立目標對象  
		LoginService loninService = new LoginServiceImpl();
		UserService userService = new UserServiceImpl();
		CglibProxy proxy = new CglibProxy();
		//建立代理對象  
		LoginService loninService$Proxy = (LoginService) proxy.newProxy(loninService);
		UserService userService$Proxy = (UserService) proxy.newProxy(userService);
		loninService$Proxy.checkUser();
		userService$Proxy.getUserName();
	}
}
2017-9-6


相關文章
相關標籤/搜索