這篇文章主要介紹“Java動態(tài)代理指的是什么”,在日常操作中,相信很多人在Java動態(tài)代理指的是什么問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Java動態(tài)代理指的是什么”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
站在用戶的角度思考問題,與客戶深入溝通,找到西和網(wǎng)站設計與西和網(wǎng)站推廣的解決方案,憑借多年的經(jīng)驗,讓設計與互聯(lián)網(wǎng)技術結合,創(chuàng)造個性化、用戶體驗好的作品,建站類型包括:做網(wǎng)站、網(wǎng)站制作、企業(yè)官網(wǎng)、英文網(wǎng)站、手機端網(wǎng)站、網(wǎng)站推廣、主機域名、虛擬空間、企業(yè)郵箱。業(yè)務覆蓋西和地區(qū)。
一、靜態(tài)代理
描述動態(tài)代理之前,先看一看靜態(tài)代理。
定義一個程序員的接口,只干兩件事情(程序員太忙,別的做不了)
Java程序員長這個樣,他會開發(fā)Java代碼,會調試Java代碼
有個很牛逼的叫 Farmerbrag 的程序員,他在開發(fā)之前,會祈禱一下,這樣他開發(fā)的代碼就不會有bug。
我們這么來描述Farmerbrag(代理類)
如果Farmerbrag只是一個普通的Java程序員,那么他的開發(fā)結果是
Farmerbrag is coding java.
Farmerbrag is debugging java.
真正的Farmerbrag(Farmerbrag代理類)是這樣的
Farmerbrag is praying for the code!
Farmerbrag is coding java.
Farmerbrag's code is bug-free and does not require debugging
靜態(tài)代理優(yōu)點不說了,說一下缺點
1、增加了代碼維護復雜度。代理類和實現(xiàn)類具有相同的接口,代理類通過實現(xiàn)類執(zhí)行具體的方法。這樣就出現(xiàn)了大量的代碼重復。如果接口增加一個方法,除了所有實現(xiàn)類需要實現(xiàn)這個方法外,所有代理類也需要實現(xiàn)此方法。
2、代理對象只服務于一種類型的對象。如果要服務多類型的對象,要為每一種對象都進行代理,靜態(tài)代理在程序規(guī)模稍大時就無法勝任。
二、動態(tài)代理的例子
接著靜態(tài)代理的例子,F(xiàn)armerbrag會祈禱這個功能是他特有的,其他程序員不會。我們可以定義一個擁有這項特技的程序員(第一部分靜態(tài)代理就是這么做的),但其他程序員可能具有別的特技,根本定義不完?。ㄓ袥]有感覺到某些業(yè)務需求很想這個場景)
其實我們不需要去定義他,這個技能可以后天習得??纯丛趺醋觯?nbsp;
這個動態(tài)代理的程序員執(zhí)行結果如下
Farmerbrag is praying for the code!
Farmerbrag is coding java.
Farmerbrag's code is bug-free and does not require debugging
farmerbragProxy的類型是Developer接口,不是一個實現(xiàn)類。farmerbrag在被代理后生成的對象,并不屬于Developer接口的任何一個實現(xiàn)類,是基于Developer接口和farmerbrag的類加載代理出來的。(mybatis定義的mapper接口怎么就能被調用執(zhí)行呢)
看一下newProxyInstance()接口的定義
包括三個參數(shù)
loader和interfaces決定這個類到底是個怎么樣的類。而h是InvocationHandler,決定這個代理類到底是多了什么功能。所以動態(tài)代理的內容重點就是這個InvocationHandler。
動態(tài)代理的例子采用了lambda表達式,主要代碼是對InvocationHandler的實現(xiàn)。
三、代碼分析
從寫代碼的角度,前面2節(jié)已經(jīng)足夠了,下邊對原理進行分析。
看看源碼其中關鍵的地方。在newProxyInstance()方法中有這樣幾段
1、克隆接口
2、查找或生成指定的代理類
3、通過反射,拿到代理類的構造函數(shù)
4、通過構造函數(shù)new一個對象,并關聯(lián)InvocationHandler
看到這里有些人可能會更蒙,InvocationHandler到底是做什么的?反射(reflect)又是怎么回事?代碼到底是怎么就被串改了呢?
四、原理剖析
1、class文件及其加載(反射)
編譯器編譯Java文件,產(chǎn)生.class 文件存放在磁盤中,文件內容是只有JVM虛擬機能夠識別的機器碼。JVM虛擬機讀取字節(jié)碼文件,取出二進制數(shù)據(jù),加載到內存中,解析.class 文件內的信息,生成對應 Class對象
通過一段代碼演示手動加載class文件字節(jié)碼到系統(tǒng)內,轉換成class對象,再實例化的過程
被加載的類我們復用前面的JavaDeveloper.class
自定義一個類加載器
執(zhí)行代碼得到如下結果
net.fengyu.proxy.JavaDeveloper
Farmerbrag is coding java.
以上代碼演示了,通過字節(jié)碼加載成class對象的過程
2、運行期生成二進制字節(jié)碼
JVM通過字節(jié)碼的二進制信息加載類,如果我們在運行期的系統(tǒng)中,遵循Java編譯系統(tǒng)組織.class文件的格式和結構,生成相應的二進制數(shù)據(jù),然后把這個二進制數(shù)據(jù)加載轉換成對應的類,這樣,就完成了在代碼中,動態(tài)創(chuàng)建一個類的能力。第二節(jié)動態(tài)代理例子顯然是這個能力的一個子集。
有一些開源框架支持運行期生成二進制字節(jié)碼這個功能,如ASM,Javassist
ASM 是一個 Java 字節(jié)碼操控框架。能夠以二進制形式修改已有類或者動態(tài)生成類。ASM可以直接產(chǎn)生二進制 class 文件,也可以在類被加載入Java虛擬機之前動態(tài)改變類行為。Spring使用的CGLIB也采用ASM框架作為其字節(jié)碼操作的工具。
下邊一段代碼生成一個跟前面JavaDeveloper幾乎一樣的類ASMDeveloper,使用上一小節(jié)的LoadClass類運行有相同的輸出
這個例子說明,在代碼里生成字節(jié)碼,并動態(tài)地加載成class對象,創(chuàng)建實例是完全可以實現(xiàn)的。動態(tài)修改某個類當然也能做到(動態(tài)代理就做這事)。
至此,原理層面基本已經(jīng)說清楚了。
3、為什么是InvocationHandler
我們已經(jīng)具有能力動態(tài)修改一個類的代碼,使用ASM哪怕生成一個非常簡單的類,代碼量也是又多又復雜。仔細思考代理模式中的代理Proxy角色。Proxy角色在執(zhí)行代理業(yè)務的時候,無非是在調用真正業(yè)務之前或者之后做一些“額外”業(yè)務。
代理類處理的邏輯很簡單,在調用某個方法前及方法后做一些額外的業(yè)務。換一種思路就是,在觸發(fā)(invoke)真實角色的方法之前或者之后做一些額外的業(yè)務。為了構造出具有通用、簡單的代理類,可以將所有的觸發(fā)真實角色動作交給一個觸發(fā)的管理器。這種管理器就是InvocationHandler。
在這種模式之中,代理Proxy和RealSubject需要實現(xiàn)相同的功能(函數(shù)方法)。
面向對象的編程之中,想要約定Proxy和RealSubject實現(xiàn)相同的功能(函數(shù)方法)有兩種方式
a、定義一個功能接口,Proxy 和RealSubject都實現(xiàn)這個接口。
b、通過繼承,Proxy繼承自RealSubject,這樣Proxy則擁有了RealSubject的功能,
JDK中提供的創(chuàng)建動態(tài)代理的機制采用a思路;而cglib采用b思路(spring兩者都使用了)。
到此,關于“Java動態(tài)代理指的是什么”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續(xù)學習更多相關知識,請繼續(xù)關注創(chuàng)新互聯(lián)網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>
文章名稱:Java動態(tài)代理指的是什么
URL網(wǎng)址:http://weahome.cn/article/pcoggc.html