中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何進行AOP開發中的Java動態代理

發布時間:2022-01-18 09:22:24 來源:億速云 閱讀:126 作者:柒染 欄目:大數據

如何進行AOP開發中的Java動態代理,針對這個問題,這篇文章詳細介紹了相對應的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。

1.AOP的概念:Aspect Oriented Programming 面向切面編程,可以通過預編譯方式和運行期動態代理實現在不修改源代碼的情況下給程序動態統一添加功能的一種技術。

2.OOP與AOP的區別:如果說面向對象編程是關注將需求功能劃分為不同的并且相對獨立,封裝良好的類,并讓它們有著屬于自己的行為,依靠繼承和多態等來定義彼此的關系的話;那么面向方面編程則是希望能夠將通用需求功能從不相關的類當中分離出來。

3.AOP的應用范圍:日志記錄,性能統計,安全控制,事務處理,異常處理等等。 

切面Aspect:切面就是一個關注點的模塊化(橫切到代碼中的關注點),譬如:事務管理、日志記錄、權限管理都是所謂的切面。 連接點Joinpoint:程序執行時的某個特定的點,在Spring中就是一個方法的執行。  
通知Advice:通知就是在切面的某個連接點上執行的操作,也就是事務管理、日志記錄等的具體代碼。 切入點Pointcut:切入點是指描述某一類特定的連接點,也就是說指定某一類要織入(Weave)通知的方法。  

如何進行AOP開發中的Java動態代理

Spring中的AOP編程的風格主要有基于XML配置文件、基于注解兩種,我們這里以注解為例。

Spring的切入點匹配表達式使用AspectJ表達式(當然也可以使用正則表達式等),也就是所謂的 Aspects,如下所示:

如何進行AOP開發中的Java動態代理

 (1.)?表示可以不配置,也就是說只有方法的名字name-pattern、方法的參數param-pattern是必須的。對于param-pattern之外的其余部分,可以使用*作為通配符,表示任意,例如:execution (* *.m(..))就是執行任意返回值、任意類中的m方法時進行織入。

  (2.)參數的通配配稍微復雜一些,其中(..)表示參數為0個或者多個,且類型任意;(*)表示任意類型的一個參數;(*,String)表示第一個參數為任意類型,第二個參數為String類型;什么都不寫表示無參數。

  (3.)多個表達式可以使用&&、||、!進行運算,因為表達式返回的是布爾值。

Spring中的AOP只針對方法,類型分為前置、后置、環繞、異常。 Spring的AOP是在運行時動態代理去完成,預編譯方式的AOP實現主要有Aspect,需要有專門JAVA編譯器,因為它是在編譯器進行處理的。  

反射機制的概念:在運行時,對于任意一個類,都能夠知道這個類的所有屬性和方法;對于任意一個對象,都能夠調用它的任意一個方法;這種動態獲取的信息以及動態調用對象的方法的功能稱為java語言的反射機制。 

說直白一些,就是我們可以于運行時加載、探知、使用編譯期間完全未知的類型,也就是具有看透任意的Class 的能力,哪怕這個Class是在運行時動態傳給你的完全未知的。
使用范圍:當你要處理的類型無法在編譯器確定,而是在運行時動態傳入的。目前的MVC框架都采用這種機制。我們在Struts2框架中,如果想接收URL上的 參數?id=1&name=2&password=3,會很自然的在Action中寫上如下的內容:
private Integer id;
private String name;
private String password;

及他們對應的set方法,然后你就可以在Action中獲取上面的三個字段的值了。可是作為Struts2框架,Apache在開發的時候這個框架的時候,怎么知道我們Action里有這幾個字段的呢?因為Struts2里使用了反射看透了你的Action。

代理設計模式:為其他對象提供一種代理以控制對這個對象的訪問。說白了就是去掉客戶不能看到的內容和服務或者增添客戶需要的額外服務。

常用的代理設計模式:

(1.)包裝:缺點就是當Animal接口中增加了新的方法,那么包裝類中也必須增加這些新的方法。
(2.)繼承:缺點更明顯,那就是不能實現對接口的所有子類的代理,與包裝的模式相比,大大縮小了代理范圍。

但不管是那種方法,都是在編譯期完成的代理,不能像Spring那樣在運行期動態的對指定的類完成代理。

JAVA自帶的動態代理是基于java.lang.reflect.Proxy、java.lang.reflect.InvocationHandler兩個類來完成的,使用JAVA反射機制。

Proxy類中的幾個方法都是靜態的,通常,你可以使用如下兩種模式創建代理對象:

如何進行AOP開發中的Java動態代理

第一種方式更加直接簡便,并且隱藏了代理$Proxy0對象的結構,回調InvocationHandler就是攔截處理的地方。

JDK的動態代理會動態的創建一個$Proxy0的類,這個類繼承了Proxy并且實現了要代理的目標對象的接口,但你不要試圖在JDK中查找這個類,因為它是動態生成的。$Proxy0的結構大致如下所示:

如何進行AOP開發中的Java動態代理

從上面的類結構,你就可以理解為什么第二種創建代理對象的方法為什么要那么寫了,以為它需要一個InvocationHandler作為構造參數。

關于回調接口InvocationHandler:

它只有一個方法invoke()需要實現,這個方法會在目標對象的方法調用的時候被激活,你可以在這里控制目標對象的方法的調用,在調用前后插入一些其他操作(譬如:鑒權、日志、事務管理等)。Invoke()方法的后兩個參數很好理解,一個是調用的方法的Method對象,另一個是方法的參數,第一個參數有些需要注意的地方,這個proxy參數就是我們使用Proxy的靜態方法創建的動態代理對象,也就是$Proxy0的實例(這點你可以在Eclipse的斷點調試中看到proxy的所屬類型確實是$Proxy0)。由于$Proxy0在JDK中不是靜態存在的,因此你不可以把第一個參數Object proxy強制轉換為$Proxy0類型,因為你根本就無法從Classpath中導入$Proxy0。那么我們可以把proxy轉為目標對象的接口嗎?因為$Proxy0是實現了目標對象的所有的接口的,答案是可以的。但實際上這樣做的意義不大,因為你會發現轉換為目標對象的接口之后,你調用接口中的任何一個方法,都會導致invoke()的調用陷入死循環而導致堆棧溢出。

如何進行AOP開發中的Java動態代理

這是因為目標對象的大部分的方法都被代理了,你在invoke()通過代理對象轉換之后的接口調用目標對象的方法,依然是走的代理對象,也就是說當mammal.type()方法被激活時會立即導致invoke()的調用,然后再次調用mammal.type()方法,… …從而使方法調用進入死循環,就像無盡的遞歸調用。

那么invoke()方法的第一個參數到底干什么用的呢?其實一般情況下這個參數都用不到,除非你想獲得代理對象的類信息描述,因為它的getClass()方法的調用不會陷入死循環。為什么getClass()不會呢?因為getClass()方法是final的,不可以被覆蓋,所以也就不會被Proxy代理。但不要認為Proxy不可以對final的方法進行動態代理,因為Proxy面向的是Monkey的接口,而不是Monkey本身,所以即便是Monkey在實現Mammal、Primate接口的時候,把方法都變為final的,也不會影響到Proxy的動態代理。

Proxy代理對象的過程如下所示

如何進行AOP開發中的Java動態代理

JDK的動態代理有個缺點,那就是不能對類進行代理,只能對接口進行代理,想象一下我們的Monkey如果沒有實現任何接口,那么將無法使用這種方式進行動態代理(實際上是因為$Proxy0這個類繼承了Proxy,JAVA的繼承不允許出現多個父類)。但準確的說這個問題不應該是缺點,因為良好的系統,每一個類都是應該有一個接口的。

從上面知道$Proxy0是動態代理對象的所屬類型,但由于這個類型根本不存在,我們如何鑒別一個對象是一個普通的對象還是動態代理對象呢?Proxy類中提供了isProxyClass(Class c)方法鑒別與此。
CGLIB是一個開源的動態代理框架,它的出現補充了JDK自帶的Proxy不能對類實現動態代理的問題。CGLIB是如何突破限制,對類也能動態代理的呢?這是因為CGLIB內部使用了另一個字節碼框架ASM,類似的字節碼框架還有Javassist、BCEL等,但ASM被認為是性能最好的一個。但這類字節碼框架要求你對JAVA的Class文件的結構、指令集都比較了解,CGLIB對外屏蔽了這些細節問題。由于CGLIB使用ASM直接操作字節碼,因此效率要比Proxy高,但這里所說的效率是指代理對象的性能,在創建代理對象時,Proxy是要比CGLIB效率高的。
CGLIB動態代理的原理是使用ASM動態生成目標對象的子類,final方法不能被子類覆蓋,自然也就不能被動態代理,這也是CGLIB的一個缺點。
CGLIB被Hibernate、Spring等很多開源框架在內部使用,用于完成對類的動態代理,Spring中的很多XML配置屬性的proxy-target-class,默認都為false,其含義就是默認不啟用對目標類的動態代理,而是對接口進行動態代理。某些情況下,如果你想對Struts2的Action或者Spring MVC的Controller進行動態代理,你會發現默認Spring會報告找不到$Proxy0的xxx方法,這是因為一般我們都不會給控制層寫一個接口,而是直接在實現類中寫請求方法,這樣JDK自帶的Proxy是找不到這些方法的,因為他們不在接口中,此時你就要設置proxy-target-class=”true”,并引入CGLIB、ASM的類庫,Spring的動態代理就可以正常工作了。

關于如何進行AOP開發中的Java動態代理問題的解答就分享到這里了,希望以上內容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關注億速云行業資訊頻道了解更多相關知識。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

大关县| 丰县| 鄂伦春自治旗| 荥阳市| 扬州市| 齐齐哈尔市| 石泉县| 大邑县| 开化县| 五指山市| 济源市| 彝良县| 大港区| 勃利县| 湘潭市| 隆回县| 西林县| 济宁市| 凤凰县| 舒城县| 沈阳市| 平山县| 民丰县| 鲁甸县| 浙江省| 黔东| 汶川县| 菏泽市| 新乐市| 贵南县| 鹤山市| 海城市| 新和县| 佳木斯市| 韩城市| 锦州市| 定西市| 铁岭市| 若尔盖县| 四子王旗| 剑阁县|