設計模式四:代理模式

代理模式的定義

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

靜態代理類:

     代理模式中的每一個代理類在編譯之後都會生成一個class文件,代理類所實現的接口和所代理的方法都被固定,這種代理被稱之爲靜態代理(Static Proxy)。那麼有沒有一種機制能夠讓系統在運行時動態創建代理類?答案動態代理(Dynamic Proxy)。

動態代理類:

在程序運行時,運用反射機制動態創建而成。

靜態代理模式下,客戶端通過Proxy類調用RealSubject類的request()方法,同時還可以在代理類中封裝其他方法(如preRequest()和postRequest()等)。如果按照這種方法使用代理模式,那麼代理類和真實主題類都應該是事先已經存在的,代理類的接口和所代理方法都已明確指定,如果需要爲不同的真實主題類提供代理類或者代理一個真實主題類中的不同方法,都需要增加新的代理類,這將導致系統中的類個數急劇增加,因此需要想辦法減少系統中類的個數。動態代理可以讓系統能夠根據實際需要來動態創建代理類,讓同一個代理類能夠代理多個不同的真實主題類而且可以代理不同的方法

現在要把一段文本發送給另一個人,普通方法是void send(File a),現在我們弄出個特性,就像 Spring AOP 那樣,在 send 之前給這個 a 壓縮一下。原來的程序沒有壓縮功能,現在我們需要添加的話而不改變原來所有的代碼的話就得用類似 AOP 這樣的代碼來處理。


動態代理使用到的類以及接口:

一、Proxy生成代理者

Proxy類提供了用於創建動態代理類和實例對象的方法,它是所創建的動態代理類的父類,它最常用的方法如下:

•       public static Class<?> getProxyClass(ClassLoaderloader,Class<?>... interfaces):該方法用於返回一個Class類型的代理類【思考,既代理類的Class,在參數中需要提供類加載器【思考既Ixxx】並需要指定代理的接口數組(與真實主題類的接口列表一致【思考,通過class獲取】)

public staticObject newProxyInstance(ClassLoader loader, Class<?>[]interfaces,InvocationHandler h):該方法用於返回一個動態創建的代理類的實例,方法中第一個參數loader表示代理類的類加載器,第二個參數interfaces表示代理類所實現的接口列表(與真實主題類的接口列表一致),第三個參數h表示所指派的調用處理程序類。

 

二、InvocationHandler調用處理器(關鍵)

生成的代理者調用方法其實就是去執行這個接口的invoke方法,而在invoke方法中我們在調用委託類的具體方法在執行這個方法之前我們可以進行相關的處理,執行後也可以作相應的處理,本質:將代理類和被代理類(真實類)關聯起來

 

       InvocationHandler接口是代理處理程序類的實現接口,每一個代理類的實例都可以提供一個相關的具體調用處理者(InvocationHandler接口的子類)。在該接口中聲明瞭如下方法:

  • public Object invoke(Objectproxy, Method method, Object[] args):該方法用於處理對代理類實例的方法調用並返回相應的結果,當一個代理實例中的業務方法被調用時將自動調用該方法。invoke()方法包含三個參數,其中第一個參數proxy表示代理類的實例第二個參數method表示需要代理的方法,第三個參數args表示代理方法的參數數組。

       動態代理類需要在運行時指定所代理真實主題類的接口,客戶端在調用動態代理對象的方法時,調用請求會將請求自動轉發給InvocationHandler對象的invoke()方法,由invoke()方法來實現對請求的統一處理。

package com.wenqing.proxy2;

public interface IGame {
	
	public void step1();
	
	public void step2();
	
	public void step3();
}

package com.wenqing.proxy2;

public class Gameplayer implements IGame{
	public static int i=0;

	@Override
	public void step1() {
		// TODO Auto-generated method stub
		i++;
		System.out.println("player is at step1");
		
	}

	@Override
	public void step2() {
		// TODO Auto-generated method stub
		i++;
		System.out.println("player is at step2");
		
	}

	@Override
	public void step3() {
		// TODO Auto-generated method stub
		System.out.println("player is at step3");
		
	}
package com.wenqing.proxy2;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Calendar;
import java.util.GregorianCalendar;

public class GameplayerHandler implements InvocationHandler{
	
	public Object obj;
	
	public GameplayerHandler(Object _obj)
	{
		this.obj=_obj;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		// TODO Auto-generated method stub
		
		beforeinvoke();
		method.invoke(obj, args);
		afterinvoke();
		return null;
	}
	
	public void beforeinvoke()
	{
		Calendar cal=new GregorianCalendar();
		
		int hour=cal.get(Calendar.HOUR_OF_DAY);
		
		int min=cal.get(Calendar.MINUTE);
		
		System.out.println("game starts at :"+hour+" :"+min);
	}
	
	public void afterinvoke()
	{
		Calendar cal=new GregorianCalendar();
		
		int hour=cal.get(Calendar.HOUR_OF_DAY);
		
		int min=cal.get(Calendar.MINUTE);
		
		System.out.println("game ends at :"+hour+" :"+min);
	}


}


測試代碼,使用JUnit;

package com.wenqing.testproxy2;

import static org.junit.Assert.*;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

import com.wenqing.proxy2.Gameplayer;
import com.wenqing.proxy2.GameplayerHandler;
import com.wenqing.proxy2.IGame;

public class Testproxy2 {

	IGame  gameplayer=new Gameplayer();
	InvocationHandler handler=new GameplayerHandler(gameplayer);
	
	IGame proxy2 = (IGame)Proxy.newProxyInstance(IGame.class.getClassLoader(), new Class[]{IGame.class}, handler);

	
	@BeforeClass
	public static void setUpBeforeClass() throws Exception {
	}

	@Before
	public void setUp() throws Exception {
	}

	@After
	public void tearDown() throws Exception {
	}

	@Test
	public final void testStep1() {
//		fail("Not yet implemented"); // TODO
		
		proxy2.step1();
		assertEquals(1,Gameplayer.i);
	}

	@Test
	public final void testStep2() {
//		fail("Not yet implemented"); // TODO
		proxy2.step2();
	}

	@Test
	public final void testStep3() {
//		fail("Not yet implemented"); // TODO
		proxy2.step3();
	}

}

思考:動態代理與靜態代理的效果區別、及場景區別;


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章