SpringBoot項(xiàng)目中JDK動(dòng)態(tài)代理和CGLIB動(dòng)態(tài)代理的使用詳解
在 Spring Boot 項(xiàng)目中,JDK 動(dòng)態(tài)代理和 CGLIB 動(dòng)態(tài)代理都是實(shí)現(xiàn) AOP (面向切面編程) 的重要技術(shù)。 它們的主要區(qū)別在于代理對象的生成方式和適用范圍。
下面詳細(xì)介紹它們的使用場景:
1. JDK 動(dòng)態(tài)代理 (JDK Dynamic Proxy)
原理:
- JDK 動(dòng)態(tài)代理是 Java 提供的內(nèi)置代理機(jī)制,它通過反射在運(yùn)行時(shí)動(dòng)態(tài)地生成代理類。
- 代理類會(huì)實(shí)現(xiàn)目標(biāo)類實(shí)現(xiàn)的接口,并將接口中的方法調(diào)用委托給一個(gè)
InvocationHandler對象來處理。 InvocationHandler接口負(fù)責(zé)實(shí)現(xiàn)具體的增強(qiáng)邏輯,例如日志記錄、安全檢查、事務(wù)管理等。
使用場景:
- 目標(biāo)類實(shí)現(xiàn)了接口: 如果目標(biāo)類實(shí)現(xiàn)了接口,則 Spring AOP 默認(rèn)使用 JDK 動(dòng)態(tài)代理。
- 簡單 AOP 場景: 適用于簡單的 AOP 場景,例如只需要對接口方法進(jìn)行增強(qiáng)的情況。
- 不需要代理類的構(gòu)造函數(shù): JDK 動(dòng)態(tài)代理創(chuàng)建代理對象時(shí),不需要調(diào)用目標(biāo)類的構(gòu)造函數(shù)。
優(yōu)點(diǎn):
- 簡單易用: JDK 動(dòng)態(tài)代理是 Java 內(nèi)置的代理機(jī)制,使用起來比較簡單。
- 不需要第三方庫: 不需要依賴第三方庫。
- 對接口友好: 代理類實(shí)現(xiàn)了目標(biāo)類實(shí)現(xiàn)的接口,符合面向接口編程的思想。
缺點(diǎn):
- 必須實(shí)現(xiàn)接口: 目標(biāo)類必須實(shí)現(xiàn)接口,否則無法使用 JDK 動(dòng)態(tài)代理。
- 只能代理接口方法: 只能代理接口中定義的方法,無法代理類自身定義的方法。
示例代碼:
// 接口
interface MyInterface {
void doSomething();
}
// 實(shí)現(xiàn)類
class MyClass implements MyInterface {
@Override
public void doSomething() {
System.out.println("MyClass is doing something...");
}
}
// 調(diào)用處理器
class MyInvocationHandler implements InvocationHandler {
private Object target; // 被代理的對象
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before method: " + method.getName()); // 前置增強(qiáng)
Object result = method.invoke(target, args); // 調(diào)用原始方法
System.out.println("After method: " + method.getName()); // 后置增強(qiáng)
return result;
}
}
// 使用 JDK 動(dòng)態(tài)代理
public class JDKDynamicProxyExample {
public static void main(String[] args) {
MyInterface target = new MyClass(); // 創(chuàng)建目標(biāo)對象
MyInvocationHandler handler = new MyInvocationHandler(target); // 創(chuàng)建調(diào)用處理器
// 創(chuàng)建代理對象
MyInterface proxy = (MyInterface) Proxy.newProxyInstance(
MyInterface.class.getClassLoader(),
new Class[] {MyInterface.class},
handler
);
proxy.doSomething(); // 調(diào)用代理對象的方法,會(huì)被攔截
}
}2. CGLIB 動(dòng)態(tài)代理 (CGLIB Dynamic Proxy)
原理:
- CGLIB (Code Generation Library) 是一個(gè)強(qiáng)大的、高性能的代碼生成庫。
- CGLIB 動(dòng)態(tài)代理通過在運(yùn)行時(shí)動(dòng)態(tài)地生成目標(biāo)類的子類來實(shí)現(xiàn)代理。
- 代理類會(huì)繼承目標(biāo)類,并重寫目標(biāo)類的方法,在重寫的方法中實(shí)現(xiàn)增強(qiáng)邏輯。
- CGLIB 動(dòng)態(tài)代理不需要目標(biāo)類實(shí)現(xiàn)接口,可以直接代理類。
使用場景:
- 目標(biāo)類沒有實(shí)現(xiàn)接口: 如果目標(biāo)類沒有實(shí)現(xiàn)接口,則 Spring AOP 使用 CGLIB 動(dòng)態(tài)代理。
- 需要代理類自身定義的方法: 需要代理類自身定義的方法,而不僅僅是接口方法。
- 需要更高的性能: 在某些情況下,CGLIB 動(dòng)態(tài)代理的性能可能比 JDK 動(dòng)態(tài)代理更好。
優(yōu)點(diǎn):
- 不需要實(shí)現(xiàn)接口: 目標(biāo)類不需要實(shí)現(xiàn)接口,可以直接代理類。
- 可以代理類自身定義的方法: 可以代理類自身定義的方法,而不僅僅是接口方法。
- 性能較好: 在某些情況下,CGLIB 動(dòng)態(tài)代理的性能可能比 JDK 動(dòng)態(tài)代理更好。
缺點(diǎn):
- 需要第三方庫: 需要依賴 CGLIB 庫。
- 實(shí)現(xiàn)復(fù)雜: CGLIB 動(dòng)態(tài)代理的實(shí)現(xiàn)比較復(fù)雜,需要生成目標(biāo)類的子類。
- final 方法無法代理: 無法代理被
final修飾的方法。 - 對構(gòu)造函數(shù)有要求: CGLIB 創(chuàng)建代理對象時(shí),需要調(diào)用目標(biāo)類的構(gòu)造函數(shù)。 如果目標(biāo)類沒有無參構(gòu)造函數(shù),則需要手動(dòng)指定構(gòu)造函數(shù)。
示例代碼:
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
// 目標(biāo)類
class MyClass {
public void doSomething() {
System.out.println("MyClass is doing something...");
}
}
// 方法攔截器
class MyMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("Before method: " + method.getName()); // 前置增強(qiáng)
Object result = proxy.invokeSuper(obj, args); // 調(diào)用原始方法
System.out.println("After method: " + method.getName()); // 后置增強(qiáng)
return result;
}
}
// 使用 CGLIB 動(dòng)態(tài)代理
public class CGLIBDynamicProxyExample {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer(); // 創(chuàng)建 Enhancer 對象
enhancer.setSuperclass(MyClass.class); // 設(shè)置超類
enhancer.setCallback(new MyMethodInterceptor()); // 設(shè)置回調(diào)
MyClass proxy = (MyClass) enhancer.create(); // 創(chuàng)建代理對象
proxy.doSomething(); // 調(diào)用代理對象的方法,會(huì)被攔截
}
}3. Spring Boot 中的 AOP 配置
在 Spring Boot 項(xiàng)目中,可以通過以下方式配置 AOP:
添加 AOP 依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>編寫切面類:
@Aspect
@Component
public class LoggingAspect {
@Before("execution(* com.example.demo.service.*.*(..))")
public void beforeAdvice(JoinPoint joinPoint) {
System.out.println("Before executing method: " + joinPoint.getSignature());
}
}配置代理方式:
- 默認(rèn)情況下,Spring AOP 會(huì)根據(jù)目標(biāo)類是否實(shí)現(xiàn)了接口來選擇使用 JDK 動(dòng)態(tài)代理或 CGLIB 動(dòng)態(tài)代理。
- 可以通過
@EnableAspectJAutoProxy注解的proxyTargetClass屬性來強(qiáng)制使用 CGLIB 動(dòng)態(tài)代理。
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true) // 強(qiáng)制使用 CGLIB 動(dòng)態(tài)代理
public class AopConfig {
// ...
}總結(jié):
| 特性 | JDK 動(dòng)態(tài)代理 | CGLIB 動(dòng)態(tài)代理 |
|---|---|---|
| 目標(biāo)類要求 | 必須實(shí)現(xiàn)接口 | 不需要實(shí)現(xiàn)接口 |
| 代理對象生成方式 | 實(shí)現(xiàn)接口 | 繼承類 |
| 性能 | 一般 | 較好 |
| 易用性 | 簡單 | 復(fù)雜 |
| 是否需要第三方庫 | 否 | 是 (net.sf.cglib) |
| 適用場景 | 目標(biāo)類實(shí)現(xiàn)了接口,簡單 AOP 場景 | 目標(biāo)類沒有實(shí)現(xiàn)接口,需要代理類自身定義的方法,性能要求較高 |
| @EnableAspectJAutoProxy | 默認(rèn)值:false | proxyTargetClass = true |
在實(shí)際開發(fā)中,Spring AOP 會(huì)自動(dòng)選擇合適的代理方式。 如果沒有特殊需求,可以使用默認(rèn)配置。
如果需要強(qiáng)制使用 CGLIB 動(dòng)態(tài)代理,可以設(shè)置
@EnableAspectJAutoProxy(proxyTargetClass = true)
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
- SpringBoot快速接入OpenAI大模型的方法(JDK8)
- SpringBoot接入deepseek深度求索示例代碼(jdk1.8)
- 關(guān)于JDK8升級17及springboot?2.x升級3.x詳細(xì)指南
- Java搭建一個(gè)springboot3.4.1項(xiàng)目?JDK21的詳細(xì)過程
- SpringBoot配置開發(fā)環(huán)境的詳細(xì)步驟(JDK、Maven、IDEA等)
- IDEA無法創(chuàng)建JDK1.8版本的Springboot項(xiàng)目問題解決(2種方法)
- 查看SpringBoot和JDK版本對應(yīng)關(guān)系的方法
相關(guān)文章
SpringBoot如何實(shí)現(xiàn)并發(fā)任務(wù)并返回結(jié)果
這篇文章主要介紹了SpringBoot如何實(shí)現(xiàn)并發(fā)任務(wù)并返回結(jié)果問題,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07
若依框架升級springBoot3啟動(dòng)druid-spring-boot-starter報(bào)錯(cuò)問題及解決
文章主要描述了在使用Spring Boot 3時(shí),由于缺少`com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties`類型的bean而報(bào)錯(cuò),解決方案是考慮在配置中定義該類型bean2026-01-01
微信商家轉(zhuǎn)賬常見的一些問題匯總(附Java調(diào)用示例)
這篇文章主要介紹了微信商家轉(zhuǎn)賬常見的一些問題匯總,文中附Java調(diào)用示例,包括IP地址不允許調(diào)用接口、APPID不存在、未獲取轉(zhuǎn)賬場景、暫不支持展示用戶收款感知、未傳入完整且對應(yīng)的轉(zhuǎn)賬場景報(bào)備信息以及商戶運(yùn)營賬戶資金不足等問題的解決步驟,需要的朋友可以參考下2025-12-12
完美解決單例設(shè)計(jì)模式中懶漢式線程安全的問題
下面小編就為大家?guī)硪黄昝澜鉀Q單例設(shè)計(jì)模式中懶漢式線程安全的問題。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-12-12
Java8通過CompletableFuture實(shí)現(xiàn)異步回調(diào)
這篇文章主要介紹了Java8通過CompletableFuture實(shí)現(xiàn)異步回調(diào),CompletableFuture是Java?8?中新增的一個(gè)類,它是對Future接口的擴(kuò)展,下文關(guān)于其更多相關(guān)詳細(xì)介紹需要的小伙伴可以參考一下2022-04-04
基于Java的界面開發(fā)詳細(xì)步驟(用戶注冊登錄)
通過一段時(shí)間Java Web的學(xué)習(xí),寫一個(gè)簡單的注冊登陸界面來做個(gè)總結(jié),這篇文章主要給大家介紹了基于Java的界面開發(fā)(用戶注冊登錄)的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-01-01
Java基礎(chǔ)學(xué)習(xí)之關(guān)鍵字和變量數(shù)據(jù)類型的那些事
變量就是系統(tǒng)為程序分配的一塊內(nèi)存單元,用來存儲(chǔ)各種類型的數(shù)據(jù),下面這篇文章主要給大家介紹了關(guān)于Java基礎(chǔ)學(xué)習(xí)之關(guān)鍵字和變量數(shù)據(jù)類型的那些事,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-07-07

