Spring AOP 支持哪兩種動(dòng)態(tài)代理方式(最新推薦)
什么是動(dòng)態(tài)代理?
動(dòng)態(tài)代理就是,在程序運(yùn)行期,創(chuàng)建目標(biāo)對(duì)象的代理對(duì)象,并對(duì)目標(biāo)對(duì)象中的方法進(jìn)行功能性增強(qiáng)的一種技術(shù)。
在生成代理對(duì)象的過(guò)程中,目標(biāo)對(duì)象不變,代理對(duì)象中的方法是目標(biāo)對(duì)象方法的增強(qiáng)方法??梢岳斫鉃檫\(yùn)行期間,對(duì)象中方法的動(dòng)態(tài)攔截,在攔截方法的前后執(zhí)行功能操作。
Spring AOP 支持以下兩種動(dòng)態(tài)代理方式:
- JDK 動(dòng)態(tài)代理 (JDK Dynamic Proxy)
- CGLIB 代理 (Code Generation Library)
Spring 框架會(huì)根據(jù)你的業(yè)務(wù)對(duì)象(目標(biāo)對(duì)象)的情況,智能地選擇其中一種來(lái)創(chuàng)建代理。
1. JDK 動(dòng)態(tài)代理
- 技術(shù)基礎(chǔ):Java 官方提供的
java.lang.reflect.Proxy類和java.lang.reflect.InvocationHandler接口,是 Java 原生支持的。 - 實(shí)現(xiàn)方式:在運(yùn)行時(shí),創(chuàng)建一個(gè)新的代理類,這個(gè)代理類會(huì)實(shí)現(xiàn)目標(biāo)對(duì)象所實(shí)現(xiàn)的所有接口。
- 硬性要求:被代理的目標(biāo)對(duì)象必須實(shí)現(xiàn)至少一個(gè)接口。 如果一個(gè)類沒(méi)有實(shí)現(xiàn)任何接口,JDK 動(dòng)態(tài)代理就無(wú)法為其創(chuàng)建代理。
- 工作流程:當(dāng)你調(diào)用代理對(duì)象的方法時(shí),這個(gè)調(diào)用會(huì)被轉(zhuǎn)發(fā)到
InvocationHandler的invoke()方法中。Spring AOP 在invoke()方法內(nèi)部織入了你的切面邏輯(如@Before、@After等),然后再通過(guò) Java 的反射機(jī)制調(diào)用原始目標(biāo)對(duì)象的方法。
2. CGLIB 代理
- 技術(shù)基礎(chǔ):一個(gè)強(qiáng)大的第三方代碼生成庫(kù)(Spring 內(nèi)部集成了它),它通過(guò)字節(jié)碼增強(qiáng) (Bytecode Enhancement) 技術(shù)來(lái)工作。
- 實(shí)現(xiàn)方式:在運(yùn)行時(shí),動(dòng)態(tài)地創(chuàng)建一個(gè)被代理對(duì)象的子類作為代理對(duì)象。
- 硬性要求:被代理的目標(biāo)類不能是
final類,需要被代理的方法也不能是final或private,因?yàn)樽宇悷o(wú)法繼承final類或重寫(xiě)final/private方法。 - 工作流程:代理類會(huì)重寫(xiě)(Override)父類(即你的目標(biāo)對(duì)象)中所有非
final的方法。在這些重寫(xiě)的方法里,Spring AOP 織入了切面邏輯,然后再通過(guò)調(diào)用super.method()來(lái)執(zhí)行原始目標(biāo)對(duì)象的業(yè)務(wù)邏輯。
Spring 如何選擇?
這是面試中的高頻問(wèn)題,因?yàn)檫@個(gè)默認(rèn)行為在不同版本中有所變化。
| 場(chǎng)景 | Spring 的選擇 |
|---|---|
| 目標(biāo)對(duì)象實(shí)現(xiàn)了接口 | 在老的 Spring 版本或傳統(tǒng) Spring XML 配置中,默認(rèn)使用 JDK 動(dòng)態(tài)代理。 在Spring Boot (2.x 及以后) 中,為了統(tǒng)一行為和解決一些代理問(wèn)題,默認(rèn)依然使用 CGLIB。 |
| 目標(biāo)對(duì)象沒(méi)有實(shí)現(xiàn)接口 | 無(wú)論在哪個(gè)版本,都只能使用 CGLIB。 |
為什么 Spring Boot 默認(rèn)使用 CGLIB?
主要原因是為了解決“方法自調(diào)用時(shí) AOP 失效”的問(wèn)題,并提供更一致的行為。使用 CGLIB 可以確保即使目標(biāo)對(duì)象實(shí)現(xiàn)了接口,代理的也是類本身,這在處理一些復(fù)雜的依賴注入和內(nèi)部調(diào)用場(chǎng)景時(shí)更加可靠。
當(dāng)然,你也可以通過(guò)在 application.properties 中進(jìn)行配置來(lái)改變這個(gè)默認(rèn)行為:
# 如果設(shè)置為 true (默認(rèn)值),則統(tǒng)一使用 CGLIB # 如果設(shè)置為 false,則在目標(biāo)對(duì)象實(shí)現(xiàn)接口時(shí),會(huì)優(yōu)先使用 JDK 動(dòng)態(tài)代理 spring.aop.proxy-target-class=true
總結(jié)對(duì)比
| 特性 | JDK 動(dòng)態(tài)代理 | CGLIB 代理 |
|---|---|---|
| 代理方式 | 基于接口 (實(shí)現(xiàn)共同接口) | 基于繼承 (創(chuàng)建子類) |
| 前提條件 | 目標(biāo)對(duì)象必須實(shí)現(xiàn)接口 | 目標(biāo)對(duì)象不能是 final 類 |
| 性能 | 在早期版本中,通過(guò)反射調(diào)用性能略低于 CGLIB。但在目前JDK 版本中,兩者性能差距已經(jīng)非常小。 | 性能通常被認(rèn)為略高,因?yàn)樗苯硬僮髯止?jié)碼并調(diào)用 super。 |
| Spring Boot 默認(rèn) | 否 | 是 |
到此這篇關(guān)于Spring AOP 支持哪兩種動(dòng)態(tài)代理方式?的文章就介紹到這了,更多相關(guān)spring aop動(dòng)態(tài)代理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
spring?boot中spring框架的版本升級(jí)圖文教程
Spring Boot是一款基于Spring框架的快速開(kāi)發(fā)框架,它提供了一系列的開(kāi)箱即用的功能和組件,這篇文章主要給大家介紹了關(guān)于spring?boot中spring框架的版本升級(jí)的相關(guān)資料,需要的朋友可以參考下2023-10-10
Spring Boot配置特定屬性spring.profiles的方法
這篇文章主要介紹了Spring Boot配置特定屬性spring.profiles的方法,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-11-11
Spring?boot?security權(quán)限管理集成cas單點(diǎn)登錄功能的實(shí)現(xiàn)
這篇文章主要介紹了Spring?boot?security權(quán)限管理集成cas單點(diǎn)登錄,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-03-03
java swing實(shí)現(xiàn)簡(jiǎn)單的五子棋游戲
這篇文章主要為大家詳細(xì)介紹了java swing實(shí)現(xiàn)簡(jiǎn)單的五子棋游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-03-03

