JAVA的靜態代理與動態代理比較

JAVA的靜態代理與動態代理比較 
一、概念
    代理模式是常用的Java 設計模式,它的特徵是代理類與委託類有同樣的接口,代理類主要負責爲委託類預處理消息、過濾消息、把消息轉發給委託類,以及事後處理消息等。代理類與委託類之間通常會存在關聯關係,一個代理類的對象與一個委託類的對象關聯,代理類的對象本身並不真正實現服務,而是通過調用委託類的對象的相關方法,來提供特定的服務。按照代理類的創建時期,代理類可分爲兩種。


靜態代理類:
     由程序員創建或由特定工具自動生成源代碼,再對其編譯。在程序運行前,代理類的.class文件就已經存在了。動態代理類:在程序運行時,運用反射機制動態創建而成。


二、靜態代理類
     如下, 靜態代理:由程序員創建或由特定工具自動生成源代碼,再對其編譯。在程序運行前,代理類的.class文件就已經存在了。 StaticProxy 類是代理類,TestProxyImpl類是委託類,這兩個類都實現了TestProxy接口。 其中TestProxyImpl類是TestProxy接口的真正實現者, 而StaticProxy類是通過調用TestProxyImpl 類的相關方法來提供特定服務的。 StaticProxy類的echo()方法和getTime()方法會分別調用被代理的TestProxyImpl 對象的echo()方法和getTime()方法,
    並且在方法調用前後都會執行一些簡單的打印操作。

public class StaticProxy implements TestProxy{
	
	TestProxy proxy;
	public StaticProxy(TestProxy proxy){
		this.proxy = proxy;
	}
	
	public static void main(String[] args) {
		TestProxy proxyImpl = new TestProxyImpl();
		TestProxy staticProxy = new StaticProxy(proxyImpl);
		System.out.println(staticProxy.echoMsg("static proxy"));
		System.out.println(staticProxy.getDate());
	}
	
	@Override
	public String echoMsg(String msg) {
		System.out.println("before calling echoMsg()...");
		proxy.echoMsg(msg);
		System.out.println("after calling echoMsg()...");
		return msg;
	}

	@Override
	public Date getDate() {
		System.out.println("before calling getDate()...");
		Date date = proxy.getDate();
		System.out.println("after calling getDate()...");
		return date;
	}

}

interface TestProxy {
	public String echoMsg(String msg);

	public Date getDate();
}

class TestProxyImpl implements TestProxy {

	@Override
	public String echoMsg(String msg) {
		System.out.println("called method:echoMsg()+" + msg);
		return msg;
	}

	@Override
	public Date getDate() {
		Date date = new Date();
		System.out.println("the date is:" + date);
		return date;
	}

}

打印結果如下:

before calling echoMsg()...
called method:echoMsg()+static proxy
after calling echoMsg()...
static proxy
before calling getDate()...
the date is:Sun Aug 03 23:11:58 CST 2014
after calling getDate()...
Sun Aug 03 23:11:58 CST 2014

由此可見,代理類可以爲委託類預處理消息、把消息轉發給委託類和事後處理消息等。


StaticProxy 類的源代碼是由程序員編寫的,在程序運行前,它的.class文件就已經存在了,這種代理類稱爲靜態代理類。


三、動態代理類
      與靜態代理類對照的是動態代理類,動態代理類的字節碼在程序運行時由Java反射機制動態生成,無需程序員手工編寫它的源代碼。動態代理類不僅簡化了編程工作,而且提高了軟件系統的可擴展性,因爲Java 反射機制可以生成任意類型的動態代理類。java.lang.reflect 包中的Proxy類和InvocationHandler 接口提供了生成動態代理類的能力。


Proxy類提供了創建動態代理類及其實例的靜態方法。
(1)getProxyClass()靜態方法負責創建動態代理類,它的完整定義如下:


public static Class<?> getProxyClass(ClassLoader loader, Class<?>[] interfaces) throws IllegalArgumentException


   參數loader 指定動態代理類的類加載器,參數interfaces 指定動態代理類需要實現的所有接口。


(2)newProxyInstance()靜態方法負責創建動態代理類的實例,它的完整定義如下:


public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler handler) throws 
      IllegalArgumentException


    參數loader 指定動態代理類的類加載器,參數interfaces 指定動態代理類需要實現的所有接口,參數handler 指定與動態代理類關聯的 InvocationHandler 對象。


以下兩種方式都創建了實現Foo接口的動態代理類的實例:
/**** 方式一 ****/
//創建InvocationHandler對象
InvocationHandler handler = new MyInvocationHandler(...);


//創建動態代理類
Class proxyClass = Proxy.getProxyClass(Foo.class.getClassLoader(), new Class[] { Foo.class });


//創建動態代理類的實例
Foo foo = (Foo) proxyClass.getConstructor(new Class[] { InvocationHandler.class }).
    newInstance(new Object[] { handler });


/**** 方式二 ****/
//創建InvocationHandler對象
InvocationHandler handler = new MyInvocationHandler(...);


//直接創建動態代理類的實例
Foo foo = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),new Class[] { Foo.class }, handler);


由Proxy類的靜態方法創建的動態代理類具有以下特點:
   動態代理類是public、final和非抽象類型的;
   動態代理類繼承了java.lang.reflect.Proxy類;
   動態代理類的名字以“$Proxy”開頭;
   動態代理類實現getProxyClass()和newProxyInstance()方法中參數interfaces指定的所有接口;


Proxy 類的isProxyClass(Class<?> cl)靜態方法可用來判斷參數指定的類是否爲動態代理類。只有通過Proxy類創建的類纔是動態代理類;


動態代理類都具有一個public 類型的構造方法,該構造方法有一個InvocationHandler 類型的參數。


由Proxy類的靜態方法創建的動態代理類的實例具有以下特點:
1. 假定變量foo 是一個動態代理類的實例,並且這個動態代理類實現了Foo 接口,那麼“foo instanceof Foo”的值爲true。把變量foo強制轉換爲Foo類型是合法的:
(Foo) foo //合法


2.每個動態代理類實例都和一個InvocationHandler 實例關聯。Proxy 類的getInvocationHandler(Object proxy)靜態方法返回與參數proxy指定的代理類實例所關聯的InvocationHandler 對象。


3.假定Foo接口有一個amethod()方法,那麼當程序調用動態代理類實例foo的amethod()方法時,該方法會調用與它關聯的InvocationHandler 對象的invoke()方法。


InvocationHandler 接口爲方法調用接口,它聲明瞭負責調用任意一個方法的invoke()方法:
Object invoke(Object proxy,Method method,Object[] args) throws Throwable


參數proxy指定動態代理類實例,參數method指定被調用的方法,參數args 指定向被調用方法傳遞的參數,invoke()方法的返回值表示被調用方法的返回值。


四、最後看一個實例:

 

public class DynamicProxy {
	/**
	 * 創建一個實現了TestProxy 接口的動態代理類的實例 參數proxy 引用被代理的TestProxy 實例
	 */
	public static TestProxy getDynamicTestProxy(final TestProxy testproxy) {
		// 創建一個實現了InvocationHandler接口的匿名類的實例
		InvocationHandler handler = new InvocationHandler() {
			@Override
			public Object invoke(Object proxy, Method method, Object[] args)
					throws Throwable {
				System.out.println("before calling " + method);// 預處理
				Object result = method.invoke(testproxy, args);// 調用被代理的TestProxy實例的方法
				System.out.println("after calling " + method);// 後處理
				return result;
			}
		};
		Class<TestProxy> classType = TestProxy.class;

		return (TestProxy) Proxy.newProxyInstance(classType.getClassLoader(),
				new Class[] { classType }, handler);
	}

	/**
	 * 類先創建了一個TestProxyImpl 實例 然後創建了一個動態代理類實例dynamicProxy 最後調用動態代理類實例的echo()方法。
	 * 
	 * @param args
	 */
	public static void main(String[] args) {
		TestProxy proxyImpl = new TestProxyImpl();
		TestProxy dynamicProxy = getDynamicTestProxy(proxyImpl);
		System.out.println("動態代理類的名字爲:" + dynamicProxy.getClass().getName());
		System.out.println(dynamicProxy.echoMsg("dynaimc proxy"));
		System.out.println(dynamicProxy.getDate());
	}
}

打印結果如下:

動態代理類的名字爲:com.practice.capter14_TI.$Proxy0
before calling public abstract java.lang.String com.practice.capter14_TI.TestProxy.echoMsg(java.lang.String)
called method:echoMsg()+dynaimc proxy
after calling public abstract java.lang.String com.practice.capter14_TI.TestProxy.echoMsg(java.lang.String)
dynaimc proxy
before calling public abstract java.util.Date com.practice.capter14_TI.TestProxy.getDate()
the date is:Sun Aug 03 23:12:57 CST 2014
after calling public abstract java.util.Date com.practice.capter14_TI.TestProxy.getDate()
Sun Aug 03 23:12:57 CST 2014

發佈了42 篇原創文章 · 獲贊 6 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章