[置頂] J2EE (九) 靜態代理和動態代理--間接「美」

        生活中有不少例子是間接來控制和訪問的,好比你找一我的不本身親自去,而是讓別人代替去作這就是最簡單的代理模式,是一種間接通訊的例子,對象間的間接通訊也一樣是面向對象設計中的一條重要的「審美觀」。間接通訊可讓對象間耦合性下降,以及易於複用的架構設計。java

        間接控制對象的交互是一個重要的編程思想,有不少的模式都體現了這種思想,好比裝飾模式、適配器模式、代理模式,都是經過間接的方式實現某一目的。編程

        這裏主要介紹一下代理模式,不管是在現實生活中仍是計算機技術中用到代理的地方很是多,主要分爲靜態代理和動態代理。數組

        咱們都作過機房收費系統就那這個系統來舉例子,這個系統中有對用戶操做的用戶接口IUser,以及實現了這個接口的類UserImp,Java代碼以下。架構

/**
 * 用戶表接口
 * @author LLS
 *
 */
public interface IUser
{
	  //添加用戶
      void addUser();
      //刪除用戶
      void delUser();
}


        用戶實現類ide

/**
 * 實現用戶接口類
 * @author LLS
 *
 */
public class UserImpl implements IUser 
{

	public void addUser() {
		// 添加用戶代碼
	}
	
	public void delUser() {
		// 刪除用戶代碼
	}
}

        

        在這個例子中,咱們可能須要在添加用戶或者刪除用戶的時候進行權限檢查,符合權限的才能執行相關動做,不然不能執行,那麼該如何修改代碼才能更加貼切,並且在實際的編寫過程當中,雖然咱們須要權限模塊,但有時候爲了更好地快速測試,咱們經常但願暫時關閉權限模塊,如何才能讓這樣的臨時需求變得更加容易處理呢?咱們如今使用代理模式來完成這樣的任務,如今繼續編寫一個類叫 UserImplProxy.函數

 

    用戶代理類post

/**
 * 用戶實現類的代理
 * @author LLS
 *
 */
public class UserImplProxy implements IUser 
{
	//對用戶實現類的引用
    private UserImpl userImpl;
    //添加用戶
	public void addUser()
	{
		//調用添加以前進行權限驗證
        preIdentify();

		if( userImpl == null )
        {
			userImpl = new UserImpl();
		}
		//調用源對象的添加
		userImpl.addUser();
		//添加完後,執行操做
        postIdentify();
	}
	
	public void delUser()
	{
        preIdentify();

		if( userImpl == null )
        {
			userImpl = new UserImpl();
		}

		userImpl.addUser();

        postIdentify();
	}
	//驗證方法
    private void preIdentify()
    {
		System.out.println("添加以前驗證代碼!");
    }
    //驗證
    private void postIdentify()
    {
		System.out.println("添加後執行操做");
    }
}

          這樣就能夠很容易的實現權限驗證功能,很靈活。測試

          可是問題又出現了,若是還有IStudent、ICard、IOnline……等不少接口,也許要一樣的權限驗證,是否是還要再爲每個接口都寫一個代理類嗎?this

          固然不是了,這個時候就須要用到動態代理了,動態代理模式能夠在程序運行時爲不少類作代理。spa

          靜態代理不足:一個被代理類對應一個代理類,當被代理類增多時,代理類會變多從而增長系統耦合度。

          爲了提升類的複用性和系統設計靈活性,使得代碼更簡潔,能夠提取高層抽象類或接口。

          Java提供了一個接口Java.lang.reflect.InvocationHandler和Proxy類支持動態代理,首先,介紹一下Proxy類,它有一個方法Static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) ,這個方法返回一個代理類的對象。

                      ClassLoader loader:指定被代理對象的類加載器。

                      Class[] interfaces:  指定被代理對象所實現的接口。

                      InvocationHandler h:指定須要調用的InvocationHandler對象。

           Java.lang.reflect.InvocationHandler接口,它只有一個方法invoke(),爲代理類的抽象方法。有三個參數

                      Object proxy代理類對象

                      Method method被代理對象的方法

                      Object[] args該方法的參數數組

           JDK中實現原理

            1.產生代理類$Proxy0類
執行了Proxy.newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)
將產生$Proxy0類,它繼承Proxy對象,並根據第二個參數,實現了被代理類的全部接口,天然就能夠生成接口要實現的全部方法了(這時候會重寫hashcode,toString和equals三個方法),可是尚未具體的實現體;

            2.將代理類$Proxy0類加載到JVM中
這時候是根據Proxy.newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)它的第一個參數----就是被代理類的類加載器,把當前的代理類加載到JVM中;

            3.建立代理類$Proxy0類的對象
調用的$Proxy0類的$Proxy0(InvocationHandler)構造函數,生成$Proxy0類的對象。參數就是Proxy.newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)它的第三個參數。這個參數就是咱們本身實現的InvocationHandler對象,咱們知道InvocationHandler對象中組合加入了代理類所代理的接口類的實現類;因此,$Proxy0對象調用全部要實現的接口的方法,都會調用InvocationHandler對象的invoke()方法實現。

           

           咱們增長ICard接口和Card類

    

package com.proxy;
/**
 * 卡接口
 * @author LLS
 *
 */
public interface ICard {
	/**
	 * 註冊卡號
	 */
	public void registerCard();
	/**
	 * 註銷卡號
	 */
	public void cancelCard();
}


           Card實現類

 

package com.proxy;

public class CardImpl implements ICard {

	@Override
	public void registerCard() {
		System.out.println("卡註冊類");
	}

	@Override
	public void cancelCard() {
		System.out.println("卡取消類");
	}

}


 

              動態代理類DynamicProxy

package com.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
 * 動態生成代理類
 * @author LLS
 *
 */
public class DynamicProxy implements InvocationHandler {
	/**
	 * 對要代理對象的引用
	 */
	private Object object=null;
	/**
	 * 給引用複製
	 * @param object
	 */
	public DynamicProxy(Object object)
	{
		this.object=object;
	}
	/**
	 * 
	 * @return
	 */
	public Object newProxyInstance(){  
	      
	     return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), this);  
	}  
	/**
	 * 經過代理對象,執行被代理對象的方法
	 */
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		//進行權限驗證
		System.out.println("執行操做以前先進行權限驗證,若是權限符合,則執行操做!");
		Object res = method.invoke(object, args);  
		
		return res;
	}

}


 

               客戶端測試類

package com.proxy;
/**
 * 客戶端類
 * @author LLS
 *
 */
public class Client
{
  	
    static public void main(String[] args)
	{ 
    	//實例化被代理對象
    	UserImpl userImpl=new UserImpl();
    	//實例化一個產生代理對象的類
    	DynamicProxy dynamicProxyUser=new DynamicProxy(userImpl);
    	//獲得代理類對象
    	IUser proxyUser=(IUser)dynamicProxyUser.newProxyInstance();
    	//經過代理調用用戶添加方法
    	proxyUser.addUser();
    	
    	//同上
    	CardImpl cardImpl=new CardImpl();
    	DynamicProxy dynamicProxyCard=new DynamicProxy(cardImpl);
    	ICard proxyCard=(ICard)dynamicProxyCard.newProxyInstance();
    	//經過代理調用,註冊卡號方法
    	proxyCard.registerCard();

	}
}


 

               運行結果爲:

 

              動態代理有點像多態同樣,能夠在程序運行時決定實例化哪個對象,多態是利用接口向上轉型來實現,以爲也能夠用反射來實現,Java中的反射是一重要機制,是不少問題變得靈活,若是對反射原理比較熟悉,那麼理解不少別的東西也會容易理解一些。

相關文章
相關標籤/搜索