Java代理模式及動態(tài)代理

代理模式

代理模式就是給某個對象提供一個代理對象,并由代理對象控制對于原對象的訪問,即客戶不直接操控原對象,而是通過代理對象間接地操控原對象。
優(yōu)勢:
給某個對象中的方法進(jìn)行擴(kuò)展,不用改變那個對象中的方法。
代理的實現(xiàn)分為:
靜態(tài)代理:代理類是在編譯時就實現(xiàn)好的。也就是說 Java 編譯完成后代理類是一個實際的 class 文件。
動態(tài)代理:代理類是在運行時生成的。也就是說 Java 編譯完之后并沒有實際的 class 文件,而是在運行時動態(tài)生成的類字節(jié)碼,并加載到JVM中。
靜態(tài)代理示例:

public class ProxyDemo {
    public static void main(String args[]){
        RealSubject subject = new RealSubject();
        Proxy p = new Proxy(subject);
        p.request();
    }
}
 
interface Subject{
    void request();
}
 
class RealSubject implements Subject{
    public void request(){
        System.out.println("request");
    }
}
 
class Proxy implements Subject{
    private Subject subject;
    public Proxy(Subject subject){
        this.subject = subject;
    }
    public void request(){
        System.out.println("PreProcess");
        subject.request();
        System.out.println("PostProcess");
    }

以上示例就是擴(kuò)展了request()方法,而對于被代理對象的request方法沒有進(jìn)行任何改動,代理類Proxy 在編譯時就已被實現(xiàn)。

動態(tài)代理

Java實現(xiàn)動態(tài)代理的大致步驟如下:
1.定義一個委托類和公共接口。
2.自己定義一個類(調(diào)用處理器類,即實現(xiàn) InvocationHandler 接口),這個類的目的是指定運行時將生成的代理類需要完成的具體任務(wù)(包括Preprocess和Postprocess),即代理類調(diào)用任何方法都會經(jīng)過這個調(diào)用處理器類。
3.生成代理對象(當(dāng)然也會生成代理類),需要為他指定委托對象,實現(xiàn)的一系列接口調(diào)用處理器類的實例。因此可以看出一個代理對象對應(yīng)一個委托對象,對應(yīng)一個調(diào)用處理器實例。
名詞說明:
委托類和委托對象:委托類是一個類,委托對象是委托類的實例。
代理類和代理對象:代理類是一個類,代理對象是代理類的實例。

Java 實現(xiàn)動態(tài)代理主要涉及以下幾個類:
1.java.lang.reflect.Proxy: 這是生成代理類的主類,通過 Proxy 類生成的代理類都繼承了 Proxy 類,即 DynamicProxyClass extends Proxy。
2.java.lang.reflect.InvocationHandler: 這里稱他為"調(diào)用處理器",他是一個接口,我們動態(tài)生成的代理類需要完成的具體內(nèi)容需要自己定義一個類,而這個類必須實現(xiàn) InvocationHandler 接口。

Proxy 類主要方法為:

//創(chuàng)建代理對象  
static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)

這個靜態(tài)函數(shù)的第一個參數(shù)是類加載器對象(即哪個類加載器來加載這個代理類到 JVM 的方法區(qū)),第二個參數(shù)是接口(表明你這個代理類需要實現(xiàn)哪些接口),第三個參數(shù)是調(diào)用處理器類實例(指定代理類中具體要干什么)。這個函數(shù)是 JDK 為了程序員方便創(chuàng)建代理對象而封裝的一個函數(shù),因此你調(diào)用newProxyInstance()時直接創(chuàng)建了代理對象(略去了創(chuàng)建代理類的代碼)。其實他主要完成了以下幾個工作:

static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler handler)
{
    //1. 根據(jù)類加載器和接口創(chuàng)建代理類
    Class clazz = Proxy.getProxyClass(loader, interfaces); 
    //2. 獲得代理類的帶參數(shù)的構(gòu)造函數(shù)
    Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class });
    //3. 創(chuàng)建代理對象,并制定調(diào)用處理器實例為參數(shù)傳入
    Interface Proxy = (Interface)constructor.newInstance(new Object[] {handler});
}

Proxy 類還有一些靜態(tài)方法,比如:
InvocationHandler getInvocationHandler(Object proxy): 獲得代理對象對應(yīng)的調(diào)用處理器對象。
Class getProxyClass(ClassLoader loader, Class[] interfaces): 根據(jù)類加載器和實現(xiàn)的接口獲得代理類。
Proxy 類中有一個映射表,映射關(guān)系為:(<ClassLoader>,(<Interfaces>,<ProxyClass>) ),可以看出一級key為類加載器,根據(jù)這個一級key獲得二級映射表,二級key為接口數(shù)組,因此可以看出:一個類加載器對象和一個接口數(shù)組確定了一個代理類。


public class DynamicProxyDemo01 {
    public static void main(String[] args) {
        RealSubject realSubject = new RealSubject();    //1.創(chuàng)建委托對象
        ProxyHandler handler = new ProxyHandler(realSubject);    //2.創(chuàng)建調(diào)用處理器對象
        Subject proxySubject = (Subject)Proxy.newProxyInstance(RealSubject.class.getClassLoader(),RealSubject.class.getInterfaces(), handler);    //3.動態(tài)生成代理對象
        proxySubject.request();    //4.通過代理對象調(diào)用方法
    }
}
 
/**
 * 接口
 */
interface Subject{
    void request();
}
 
/**
 * 委托類
 */
class RealSubject implements Subject{
    public void request(){
        System.out.println("====RealSubject Request====");
    }
}
/**
 * 代理類的調(diào)用處理器
 */
class ProxyHandler implements InvocationHandler{
    private Subject subject;
    public ProxyHandler(Subject subject){
        this.subject = subject;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out.println("====before====");//定義預(yù)處理的工作,當(dāng)然你也可以根據(jù) method 的不同進(jìn)行不同的預(yù)處理工作
        Object result = method.invoke(subject, args);
        System.out.println("====after====");
        return result;
    }
}

InvocationHandler 接口中有方法:

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

這個函數(shù)是在代理對象調(diào)用任何一個方法時都會調(diào)用的,方法不同會導(dǎo)致第二個參數(shù)method不同,第一個參數(shù)是代理對象(表示哪個代理對象調(diào)用了method方法),第二個參數(shù)是 Method 對象(表示哪個方法被調(diào)用了),第三個參數(shù)是指定調(diào)用方法的參數(shù)。

動態(tài)生成的代理類具有幾個特點:
1.繼承 Proxy 類,并實現(xiàn)了在Proxy.newProxyInstance()中提供的接口數(shù)組。
2.public final。
3.命名方式為 ProxyN,其中N會慢慢增加,一開始是Proxy1,接下來是$Proxy2...
有一個參數(shù)為 InvocationHandler 的構(gòu)造函數(shù)。這個從 Proxy.newProxyInstance() 函數(shù)內(nèi)部的clazz.getConstructor(new Class[] { InvocationHandler.class }) 可以看出。

Java 實現(xiàn)動態(tài)代理的缺點:因為 Java 的單繼承特性(每個代理類都繼承了 Proxy 類),只能針對接口創(chuàng)建代理類,不能針對類創(chuàng)建代理類。

Java 動態(tài)代理的內(nèi)部實現(xiàn)(利用Java反射機(jī)制):

Java 是怎么保證代理對象調(diào)用的任何方法都會調(diào)用 InvocationHandler 的 invoke() 方法,
這就涉及到動態(tài)代理的內(nèi)部實現(xiàn)。假設(shè)有一個接口 Subject,且里面有 int request(int i) 方法,則生成的代理類大致如下:


public final class $Proxy1 extends Proxy implements Subject{
    private InvocationHandler h;
    private $Proxy1(){}
    public $Proxy1(InvocationHandler h){
        this.h = h;
    }
    public int request(int i){
        Method method = Subject.class.getMethod("request", new Class[]{int.class});    //創(chuàng)建method對象
        return (Integer)h.invoke(this, method, new Object[]{new Integer(i)}); //調(diào)用了invoke方法
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容