天天看點

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代碼生成庫簡介

首頁:https://github.com/cglib/cglib

wiki位址:https://github.com/cglib/cglib/wiki 

jar包下載下傳位址、源碼下載下傳位址

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

API文檔詳見:http://cglib.sourceforge.net/apidocs/index.html 

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

來自為知筆記(Wiz)

本文來自部落格園,作者:白乾濤,轉載請注明原文連結:https://www.cnblogs.com/baiqiantao/p/7485704.html

繼續閱讀