天天看點

【spring架構】動态代理的學習(轉)

      以下的内容部分參考了網絡上的内容,在此對原作者表示感謝! 

        java中動态代理的實作,關鍵就是這兩個東西:proxy、invocationhandler,下面從invocationhandler接口中的invoke方法入手,簡單說明一下java如何實作動态代理的。 

        首先,invoke方法的完整形式如下: 

        首先猜測一下,method是調用的方法,即需要執行的方法;args是方法的參數;proxy,這個參數是什麼?以上invoke()方法的實作即是比較标準的形式,我們看到,這裡并沒有用到proxy參數。檢視jdk文檔中proxy的說明,如下: 

        由此可以知道以上的猜測是正确的,同時也知道,proxy參數傳遞的即是代理類的執行個體。 

        為了友善說明,這裡寫一個簡單的例子來實作動态代理。 

運作結果如下:此處省略了包名,***代替  

true  

subject的class類是:class $proxy0  

subject中的屬性有:m1, m3, m0, m2,   

subject中的方法有:request, hashcode, equals, tostring,   

subject的父類是:class java.lang.reflect.proxy  

subject實作的接口是:cn.edu.ustc.dynamicproxy.subject,   

運作結果為:  

before calling public abstract void ***.subject.request()  

from real subject.  

after calling public abstract void ***.subject.request()  

ps:這個結果的資訊非常重要,至少對我來說。因為我在動态代理犯暈的根源就在于将上面的subject.request()了解錯了,至少是被表面所迷惑,沒有發現這個subject和proxy之間的聯系,一度糾結于最後調用的這個request()是怎麼和invoke()聯系上的,而invoke又是怎麼知道request存在的。其實上面的true和class $proxy0就能解決很多的疑問,再加上下面将要說的$proxy0的源碼,完全可以解決動态代理的疑惑了。 

        從以上代碼和結果可以看出,我們并沒有顯示的調用invoke()方法,但是這個方法确實執行了。下面就整個的過程進行分析一下: 

        從client中的代碼看,可以從newproxyinstance這個方法作為突破口,我們先來看一下proxy類中newproxyinstance方法的源代碼: 

        proxy.newproxyinstance(classloader loader, class<?>[] interfaces, invocationhandler h)做了以下幾件事. 

        (1)根據參數loader和interfaces調用方法 getproxyclass(loader, interfaces)建立代理類$proxy0.$proxy0類 實作了interfaces的接口,并繼承了proxy類. 

        (2)執行個體化$proxy0并在構造方法中把dynamicsubject傳過去,接着$proxy0調用父類proxy的構造器,為h指派,如下: 

        來看一下這個繼承了proxy的$proxy0的源代碼: 

        接着把得到的$proxy0執行個體強制轉換成subject,并将引用賦給subject。當執行subject.request()方法時,就調用了$proxy0類中的request()方法,進而調用父類proxy中的h的invoke()方法.即invocationhandler.invoke()。 

ps:1、需要說明的一點是,proxy類中getproxyclass方法傳回的是proxy的class類。之是以說明,是因為我一開始犯了個低級錯誤,以為傳回的是“被代理類的class類”- -!推薦看一下getproxyclass的源碼,很長=。= 

        2、從$proxy0的源碼可以看出,動态代理類不僅代理了顯示定義的接口中的方法,而且還代理了java的根類object中的繼承而來的equals()、hashcode()、tostring()這三個方法,并且僅此三個方法。 

q:到現在為止,還有一個疑問,invoke方法中的第一個參數是proxy的執行個體(準确說,最終用到的是$proxy0的執行個體),但是有什麼用呢?或者說,程式内是怎樣顯示出作用的? 

a:就本人目前的水準看來,這個proxy參數并沒有什麼作用,在整個動态代理機制中,并沒有用到invocationhandler中invoke方法的proxy參數。而傳入的這個參數實際是代理類的一個執行個體。我想可能是為了讓程式員在invoke方法中使用反射來擷取關于代理類的一些資訊吧。