Java 代理模式(二) Java中的動態代理

Java中的動態代理

 

動態代理類

  Java動態代理類位於java.lang.reflect包下,一般主要涉及到以下兩個類:

  1.Interface InvocationHandler(調用處理器

  接口說明:InvocationHandler 是由  一個代理實例的調用處理器  實現的接口。

每一個代理實例都會有一個與之關聯的 調用處理器。當我們調用某一個代理實例上的某一個方法的時候,這個方法調用就會被編碼並派發到與之關聯的調用處理器的 invoke() 方法上。

該接口中僅定義了一個方法:

  Object invoke(Object proxy, Method method, Object[]  args) 

  方法說明:處理一個代理實例上的方法調用,並返回結果。 當我們調用一個代理實例上的某一個方法的時候,與之關聯的調用處理器上的invoke()方法就會被執行。

在實際使用時,第一個參數obj一般是指代理類,method是被代理的方法,如上例中的request(),args爲該方法的參數數組(無參時設置爲null)。

  這個抽象方法在代理類中動態實現。

  2.Proxy

類說明:Proxy 爲創建動態代理類和實例 提供靜態方法,它還是被那些靜態方法創建的動態代理類的父類。

該類即爲動態代理類,作用類似於上文例子中的ProxySubject,其中主要包含如下內容:

  protected  Proxy(InvocationHandler h): 構造函數,用於給內部的invocation handler賦值。

  static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) : loader是類裝載器,interfaces是真實類所擁有的全部接口的數組。

  static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)  :返回代理類的一個實例,返回後的代理類可以當作被代理類使用(可使用被代理類在Subject接口中聲明過的方法)。

 

動態代理類說明

  所謂Dynamic Proxy是這樣一種class:

  它是在運行時生成的class,在生成它時你必須提供一組interface給它,然後該class就宣稱它實現了這些interface。

  你當然可以把該class的實例當作這些interface中的任何一個來用。

  當然,這個Dynamic Proxy其實就是一個Proxy,它不會替你做實質性的工作,在生成它的實例時你必須提供一個handler,由它接管實際的工作。

  在使用動態代理類時,我們必須實現InvocationHandler接口。每一個動態代理類都會有一個與之關聯的invocation handler。

  真正的調用是在invocation handler的invoke()方法裏完成的。

 

實例說明:

  首先定義抽象角色和真實角色類:

public interface Subject
{
    public void request();
}
複製代碼
public class RealSubject implements Subject
{
    @Override
    public void request()
    {
        System.out.println("From real subject!");
    }
}
複製代碼

  之後定義一個DynamicSubject類:

複製代碼
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * 該代理類的內部屬性是Object類型,實際使用的時候通過該類的構造方法傳遞進來一個對象。
 * 該類實現了invoke()方法,該方法中的method.invoke()其實就是調用被代理對象的將要執行的方法,
 * 方法參數sub表示該方法從屬於sub。
 * 通過動態代理類,我們可以在執行真實對象的方法前後加入自己的一些額外方法
 *
 */
public class DynamicSubject implements InvocationHandler
{

    //對真實對象的引用
    private Object sub;
    
    public DynamicSubject(Object obj)
    {
        this.sub = obj;
        
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable
    {
        System.out.println("Before calling: " + method);
        
        //通過反射來調用方法
        method.invoke(sub, args);
        
        System.out.println("After calling: " + method);
        return null;
    }

}
複製代碼

  使用的時候:

複製代碼
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class Client
{
    public static void main(String[] args)
    {
        RealSubject realSubject = new RealSubject();

        InvocationHandler handler = new DynamicSubject(realSubject);

        Class<?> classType = handler.getClass();

	// 下面的代碼一次性生成代理
        // $Proxy0是在運行期間動態生成的一個類
        // 動態生成一個類(實現了指定的接口),生成類的對象,實現真實對象所實現的接口,強制轉換成接口類型(Subject)
        // subject是 $Proxy0 類型的一個實例
        Subject subject = (Subject) Proxy.newProxyInstance(classType
                .getClassLoader(), realSubject.getClass().getInterfaces(),
                handler);
	
	// 調用方法時,轉移給handler接管,由其中的invoke()方法實際完成方法執行
subject.request();
	// 打印出:class $Proxy0
System.out.println(subject.getClass()); }}
複製代碼

  通過這種方式,被代理的對象(RealSubject)可以在運行時動態改變,需要控制的接口(Subject接口)可以在運行時改變,控制的方式(DynamicSubject類)也可以動態改變,從而實現了非常靈活的動態代理關係。

 

動態代理

  動態代理是指客戶通過代理類來調用其他對象的方法。

  動態代理使用場合:

    調試。

    遠程方法調用(RMI)。

 

動態代理步驟

  1.創建一個實現接口InvocationHandler的類,它必須實現invoke()方法。

  2.創建被代理的類以及接口。

  3.通過Proxy的靜態方法newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)創建一個代理。

  4.通過代理調用方法。

 

動態代理實現例子2:  

複製代碼
import java.util.List;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Vector;

public class VectorProxy implements InvocationHandler
{
    private Object proxyObj;

    public VectorProxy(Object obj)
    {
        this.proxyObj = obj;
    }

    public static Object factory(Object obj)
    {
        Class<?> classType = obj.getClass();

        return Proxy.newProxyInstance(classType.getClassLoader(),
                classType.getInterfaces(), new VectorProxy(obj));
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable
    {
        System.out.println("Before calling: " + method);

        // 打印出方法參數
        if (args != null)
        {
            for (Object obj : args)
            {
                System.out.println(obj);
            }
        }

        // 調用方法
        Object object = method.invoke(proxyObj, args);

        System.out.println("After calling: " + method);

        return object;
    }

    public static void main(String[] args)
    {
        List v = (List) factory(new Vector());
        
        System.out.println(v.getClass().getName());
        
        v.add("New");
        v.add("York");
        
        System.out.println(v);
        
        v.remove(0);
        System.out.println(v);

    }

}
複製代碼

 

動態代理實現例子3:

  這個例子中定義了一個接口: 

public interface Foo
{
    public void doAction();
}

  這個接口有兩個實現類:

複製代碼
public class FooImpl1 implements Foo
{
    @Override
    public void doAction()
    {
        System.out.println("From Implement 1 !");
    }
}

public class FooImpl2 implements Foo
{
    @Override
    public void doAction()
    {
        System.out.println("From Implement 2 !");
    }
}
複製代碼

  定義invocation handler,其中的set方法使得實際對象是可更換的:

複製代碼
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class CommonInvocationHandler implements InvocationHandler
{
    private Object target;

    public CommonInvocationHandler()
    {

    }

    public CommonInvocationHandler(Object obj)
    {
        this.target = obj;
    }

    public void setTarget(Object target)
    {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable
    {        
        return method.invoke(target, args);
    }

}
複製代碼

  使用:

複製代碼
import java.lang.reflect.Proxy;

public class Demo
{
    public static void main(String[] args)
    {
        CommonInvocationHandler handler = new CommonInvocationHandler();

        Foo f = null;

        handler.setTarget(new FooImpl1());

        f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
                new Class[] { Foo.class }, handler);
        
        f.doAction();
        System.out.println("----------------------------");
        handler.setTarget(new FooImpl2());    
        f.doAction();

    }

}
複製代碼

  程序運行後輸出:

From Implement 1 !
----------------------------
From Implement 2 !

  

 

參考資料

  聖思園張龍老師Java SE系列視頻。

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