之前介紹的反射和注解都是Java中的動(dòng)態(tài)特性,還有即將介紹的動(dòng)態(tài)代理也是Java中的一個(gè)動(dòng)態(tài)特性。這些動(dòng)態(tài)特性使得我們的程序很靈活。動(dòng)態(tài)代理是面向AOP編程的基礎(chǔ)。通過(guò)動(dòng)態(tài)代理,我們可以在運(yùn)行時(shí)動(dòng)態(tài)創(chuàng)建一個(gè)類,實(shí)現(xiàn)某些接口中的方法,目前為止該特性已被廣泛應(yīng)用于各種框架和類庫(kù)中,例如:Spring,Hibernate,MyBatis等。理解動(dòng)態(tài)代理是理解框架底層的基礎(chǔ)。
     主要內(nèi)容如下:

  • 理解代理是何意

  • Java SDK實(shí)現(xiàn)動(dòng)態(tài)代理

  • 第三方庫(kù)cglib實(shí)現(xiàn)動(dòng)態(tài)代理

一、代理的概念
     單從字面上理解,代理就是指原對(duì)象的委托人,它不是原對(duì)象但是卻有原對(duì)象的權(quán)限。Java中的代理意思類似,就是指通過(guò)代理來(lái)操作原對(duì)象的方法和屬性,而原對(duì)象不直接出現(xiàn)。這樣做有幾點(diǎn)好處:

  • 節(jié)省創(chuàng)建原對(duì)象的高開(kāi)銷,創(chuàng)建一個(gè)代理并不會(huì)立馬創(chuàng)建一個(gè)實(shí)際原對(duì)象,而是保存一個(gè)原對(duì)象的地址,按需加載

  • 執(zhí)行權(quán)限檢查,保護(hù)原對(duì)象

photoshop培訓(xùn),電腦培訓(xùn),電腦維修培訓(xùn),移動(dòng)軟件開(kāi)發(fā)培訓(xùn),網(wǎng)站設(shè)計(jì)培訓(xùn),網(wǎng)站建設(shè)培訓(xùn)

實(shí)際上代理堵在了原對(duì)象的前面,在代理的內(nèi)部往往還是調(diào)用了原對(duì)象的方法,只是它還做了其他的一些操作。下面看第一種實(shí)現(xiàn)動(dòng)態(tài)代理的方式。

二、Java SDK實(shí)現(xiàn)動(dòng)態(tài)代理
     實(shí)現(xiàn)動(dòng)態(tài)代理主要有如下幾個(gè)步驟:

  • 實(shí)現(xiàn) InvocationHandler接口,完成自定義調(diào)用處理器

  • 通過(guò)Proxy的getProxyClass方法獲取對(duì)應(yīng)的代理類

  • 利用反射技術(shù)獲取該代理類的constructor構(gòu)造器

  • 利用constructor構(gòu)造代理實(shí)例對(duì)象

在一步步解析源碼之前,我們先通過(guò)一個(gè)完整的實(shí)例了解下,整個(gè)程序的一步步邏輯走向。

//定義了一個(gè)調(diào)用處理器public class MyInvotion implements InvocationHandler {    private Object realObj;    public MyInvotion(Object obj){        this.realObj =  obj;
    }    
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{        //通過(guò)代理執(zhí)行原對(duì)象的方法
        return method.invoke(realObj,args);
    }
}
//定義一個(gè)接口public interface MyInterface {    
    public void sayHello();
}//該接口的一個(gè)實(shí)現(xiàn)類,該類就是我們的原對(duì)象public class ClassA implements MyInterface {    public void sayHello(){
        System.out.println("hello walker");
    }
}
//main 函數(shù)public static void main(String[] args){
        ClassA a = new ClassA();
        MyInvotion myInvotion = new MyInvotion(a);
        Class myProxy = Proxy.getProxyClass(ClassA.class.getClassLoader(), new Class[]{MyInterface.class});        Constructor constructor =myProxy.getConstructor(new Class[]{InvocationHandler.class});
        MyInterface m = (MyInterface)constructor.newInstance(myInvotion);
        m.sayHello();
    }
輸出結(jié)果:hello walker

簡(jiǎn)單說(shuō)下整體的運(yùn)行過(guò)程,首先我們創(chuàng)建ClassA 實(shí)例并將它傳入自定義的調(diào)用處理器MyInvotion,在MyInvotion中用realObj接受該參數(shù)代表原對(duì)象。接著調(diào)用Proxy的getProxyClass方法,將ClassA 的類加載器和ClassA 的實(shí)現(xiàn)的接口集合傳入,該方法內(nèi)部會(huì)實(shí)現(xiàn)所有接口返回該類的代理類,然后我們利用反射獲取代理類的構(gòu)造器并創(chuàng)建實(shí)例。

以上便是整個(gè)程序運(yùn)行的大致流程,接下來(lái)我們從源代碼的角度看看具體是如何實(shí)現(xiàn)的。首先我們看InvocationHandler接口,這是我們的調(diào)用處理器,在代理類中訪問(wèn)的所有的方法都會(huì)被轉(zhuǎn)發(fā)到這執(zhí)行,具體的等我們看了代理類源碼及理解了。該接口中唯一的方法是:

public Object invoke(Object proxy, Method method, Object[] args)
  • 參數(shù)Proxy表示動(dòng)態(tài)生成的代理類的對(duì)象,基本沒(méi)啥用

  • 參數(shù)method表示當(dāng)前正在被調(diào)用的方法

  • 數(shù)組args指定了該方法的參數(shù)集合

我們上例中對(duì)該接口的實(shí)現(xiàn)情況,定義了一個(gè)realObj用于保存原對(duì)象的引用。重寫(xiě)的invoke方法中調(diào)用了原對(duì)象realObj的method方法,具體誰(shuí)來(lái)調(diào)用該方法以及傳入的參數(shù)是什么,在看完代理類源碼即可知曉。

接下來(lái)我們看看最核心的內(nèi)容,如何動(dòng)態(tài)創(chuàng)建代理類。這是getProxyClass方法的源碼:

    public static Class<?> getProxyClass(ClassLoader loader,                                         Class<?>... interfaces)
        throws IllegalArgumentException
    {        final Class<?>[] intfs = interfaces.clone();        final SecurityManager sm = System.getSecurityManager();        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }        return getProxyClass0(loader, intfs);
    }

首先獲取了該類實(shí)現(xiàn)的所有的接口的集合,然后判斷創(chuàng)建該代理是否具有安全性問(wèn)題,檢查接口類對(duì)象是否對(duì)類裝載器可見(jiàn)等。然后調(diào)用另外一個(gè)getProxyClass0方法,我們跟進(jìn)去:

    private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {        if (interfaces.length > 65535) {            throw new IllegalArgumentException("interface limit exceeded");
        }        // If the proxy class defined by the given loader implementing
        // the given interfaces exists, this will simply return the cached copy;
        // otherwise, it will create the proxy class via the ProxyClassFactory
        return proxyClassCache.get(loader, interfaces);
    }

判斷如果該類的接口超過(guò)65535(想必還沒(méi)有那么牛的類),拋出異常。在我們的Proxy類中有個(gè)屬性proxyClassCache,這是一個(gè)WeakCache類型的靜態(tài)變量。它指示了我們的類加載器和代理類之間的映射。所以proxyClassCache的get方法用于根據(jù)類加載器來(lái)獲取Proxy類,如果已經(jīng)存在則直接從cache中返回,如果沒(méi)有則創(chuàng)建一個(gè)映射并更新cache表。具體創(chuàng)建一個(gè)Proxy類并存入cache表中的代碼限于能力,未能參透。

至此我們就獲取到了該ClassA類對(duì)應(yīng)的代理類型,接著我們通過(guò)該類的getConstructor方法獲取該代理類的構(gòu)造器,并傳入InvocationHandler.class作為參數(shù),至于為何要傳入該類型作為參數(shù),等會(huì)看代理類源碼變一目了然了。

最后newInstance創(chuàng)建該代理類的實(shí)例,實(shí)現(xiàn)對(duì)ClassA對(duì)象的代理。

可能看完上述的介紹,你還會(huì)有點(diǎn)暈。下面我們通過(guò)查看動(dòng)態(tài)生成的代理類的源碼來(lái)加深理解。上述getProxyClass方法會(huì)動(dòng)態(tài)創(chuàng)建一個(gè)代理類并返回他的Class類型,這個(gè)代理類一般被命名為$ProxyN,這個(gè)N是遞增的用于標(biāo)記不同的代理類。我們可以利用反編譯工具反編譯該class:

final class $Proxy0 extends Proxy implements MyInterface {    private static Method m1;    private static Method m3;    private static Method m2;    private static Method m0;    public $Proxy0(InvocationHandler paramInvocationHandler) {        super(paramInvocationHandler);
    }    public final boolean equals(Object paramObject) {        return ((Boolean) this.h.invoke(this, m1, 
                new Object[] { paramObject })).booleanValue();
    }    public final void sayHello() {        this.h.invoke(this, m3, null);
    }    public final String toString() {        return (String) this.h.invoke(this, m2, null);
    }    public final int hashCode() {        return ((Integer) this.h.invoke(this, m0, null)).intValue();
    }    static {
        m1 = Class.forName("java.lang.Object").getMethod("equals",                new Class[] { Class.forName("java.lang.Object") });
        m3 = Class.forName("laoma.demo.proxy.SimpleJDKDynamicProxyDemo$IService")
                .getMethod("sayHello",new Class[0]);
        m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
        m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
    }
}

這就是上述的ClassA的動(dòng)態(tài)代理類,我們看到該類的構(gòu)造方法傳入?yún)?shù)InvocationHandler類型,并調(diào)用了父類Proxy的構(gòu)造方法保存了這個(gè)InvocationHandler實(shí)例,這也解釋了我們?yōu)槭裁丛讷@取構(gòu)造器的時(shí)候需要指定參數(shù)類型為InvocationHandler,就是因?yàn)閯?dòng)態(tài)代理類只有一個(gè)構(gòu)造器并且參數(shù)類型為InvocationHandler。

接著我們看其中的方法,貌似只有一個(gè)sayHello是我們知道的,別的方法哪來(lái)的?我們說(shuō)過(guò)在動(dòng)態(tài)創(chuàng)建代理類的時(shí)候,會(huì)實(shí)現(xiàn)原對(duì)象的所有接口。所以sayHello方法是實(shí)現(xiàn)的MyInterface。而其余的四個(gè)方法是代理類由于比較常用,被默認(rèn)添加到其中。而這些方法的內(nèi)部都是調(diào)用的this.h.invoke這個(gè)方法,this.h就是保存在父類Proxy中的InvocationHandler實(shí)例(我們用構(gòu)造器向其中保存的),調(diào)用了這個(gè)類的invoke方法,在我們自定義的InvocationHandler實(shí)例中重寫(xiě)了invoke方法,我們寫(xiě)的比較簡(jiǎn)單,直接執(zhí)行傳入的method。

也就是我們調(diào)用代理類的任何一個(gè)方法都會(huì)轉(zhuǎn)發(fā)到該InvocationHandler實(shí)例中的involve中,因?yàn)樵搶?shí)例中保存有我們的原對(duì)象,所以我們可以選擇直接調(diào)取原對(duì)象中的方法作為回調(diào)。

以上便是有關(guān)Java SDK中動(dòng)態(tài)代理的相關(guān)內(nèi)容,稍微總結(jié)下,首先我們通過(guò)實(shí)現(xiàn)InvocationHandler自定義一個(gè)調(diào)用處理類,該類中會(huì)保存我們的原對(duì)象,并提供一個(gè)invoke方法供代理類使用。然后我們通過(guò)getProxyClass方法動(dòng)態(tài)創(chuàng)建代理類,最后用反射獲取代理類的實(shí)例對(duì)象。

需要注意的是:以上我們使用的四步創(chuàng)建代理實(shí)例時(shí)最根本的,其實(shí)Proxy中提供一個(gè)方法可以封裝2到4步的操作。上述代碼也可以這么寫(xiě):

ClassA a = new ClassA();
MyInterface aProxy = (MyInterface)Proxy.newProxyInstance(ClassA.class.getClassLoader(),new Class<?>[]{MyInterface.class},new MyInvotion(a));
aProxy.sayHello();

我們打開(kāi)該方法的內(nèi)部源碼,其實(shí)走的還是我們上述的過(guò)程,它就是做了封裝。

public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h){

    Objects.requireNonNull(h);        
        //獲取所有接口
        final Class<?>[] intfs = interfaces.clone();        final SecurityManager sm = System.getSecurityManager();        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }        
        //創(chuàng)建動(dòng)態(tài)代理類
        Class<?> cl = getProxyClass0(loader, intfs);        try {            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }            final Constructor<?> cons = cl.getConstructor(constructorParams);            final InvocationHandler ih = h;            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {                    public Void run() {
                        cons.setAccessible(true);                        return null;
                    }
                });
            }            return cons.newInstance(new Object[]{h});
    .............
    ..............
}

二、第三方庫(kù)cglib實(shí)現(xiàn)動(dòng)態(tài)代理
     使用動(dòng)態(tài)代理,我們編寫(xiě)通用的代碼邏輯,即僅實(shí)現(xiàn)一個(gè)InvocationHandler實(shí)例完成對(duì)多個(gè)類型的代理。但是我們從動(dòng)態(tài)生成的代理類的源碼可以看到,所有的代理類都繼承自Proxy這個(gè)類,這就導(dǎo)致我們這種方式不能代理類,只能代理接口。因?yàn)閖ava中是單繼承的。也就是說(shuō),給我們一個(gè)類型,我們只能動(dòng)態(tài)實(shí)現(xiàn)該類所有的接口類型,但是該類繼承的別的類我們?cè)诖眍愔惺遣荒苁褂玫?,因?yàn)樗鼪](méi)有被代理類繼承。下面看個(gè)例子:

public class ClassB {    public void welcome(){
        System.out.println("welcom walker");
    }
}public interface MyInterface {    public void sayHello();
}//需要被代理的原類型,繼承了ClassB和接口MyInterface public class ClassA extends ClassB implements MyInterface {    public void sayHello(){
        System.out.println("hello walker");
    }
}
//InvocationHandler 實(shí)例public class MyInvotion implements InvocationHandler {private Object realObj;public MyInvotion(Object obj){     this.realObj =  obj;
}public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{        return method.invoke(realObj,args);
    }
}

我們反編譯該代理類,和上述的源碼是一樣的,此處不再重復(fù)貼出。我們能從中看出來(lái)的是,我們的代理只會(huì)實(shí)現(xiàn)原類型中所有的接口,至于原類型所繼承的類,在生成Proxy代理類的時(shí)候會(huì)丟棄,因?yàn)樗械拇眍惐仨毨^承Proxy類,這就導(dǎo)致原類型的父類中的方法 在代理類中丟失。這是該種方式的一大弊端。下面我們看看另一種方式實(shí)現(xiàn)動(dòng)態(tài)代理,該種方式完美解決了這種不足。

限于篇幅,我們下篇介紹cglib實(shí)現(xiàn)動(dòng)態(tài)代理機(jī)制的內(nèi)容,本篇暫時(shí)結(jié)束,總結(jié)的不好,望海涵。

http://www.cnblogs.com/yangming1996/p/6820180.html