JDK動(dòng)態(tài)代理提高代碼可維護(hù)性和復(fù)用性利器
1. 什么是 JDK 動(dòng)態(tài)代理?
JDK 動(dòng)態(tài)代理是 Java 中一種實(shí)現(xiàn)代理模式的機(jī)制。它允許在運(yùn)行時(shí)創(chuàng)建代理類和對(duì)象,用于替代原始對(duì)象進(jìn)行方法調(diào)用,并可以在方法調(diào)用前后添加額外的邏輯。
2. 為什么需要 JDK 動(dòng)態(tài)代理?
使用動(dòng)態(tài)代理可以實(shí)現(xiàn)橫切關(guān)注點(diǎn)(cross-cutting concerns)的分離,例如日志記錄、性能監(jiān)控、事務(wù)管理等。通過(guò)將這些通用的功能從業(yè)務(wù)邏輯中抽離出來(lái),可以提高代碼的可維護(hù)性和復(fù)用性。
另外,動(dòng)態(tài)代理還可以用于解耦合,當(dāng)我們需要對(duì)某個(gè)接口的多個(gè)實(shí)現(xiàn)類進(jìn)行統(tǒng)一處理時(shí),可以使用動(dòng)態(tài)代理來(lái)實(shí)現(xiàn),而不需要修改原有的代碼。
3. JDK 動(dòng)態(tài)代理的實(shí)現(xiàn)原理?
JDK 動(dòng)態(tài)代理基于 Java 的反射機(jī)制實(shí)現(xiàn)。當(dāng)我們使用 JDK 動(dòng)態(tài)代理時(shí),首先需要定義一個(gè)接口,然后創(chuàng)建一個(gè)實(shí)現(xiàn)了 InvocationHandler 接口的代理類。在代理類中,我們可以指定要代理的目標(biāo)對(duì)象以及在方法調(diào)用前后執(zhí)行的邏輯。當(dāng)客戶端調(diào)用代理對(duì)象的方法時(shí),代理對(duì)象會(huì)將方法調(diào)用轉(zhuǎn)發(fā)給 InvocationHandler 的 invoke 方法,在該方法中可以根據(jù)需要執(zhí)行相應(yīng)的邏輯。
具體的實(shí)現(xiàn)步驟如下:
- 定義一個(gè)接口,該接口是被代理對(duì)象和代理對(duì)象共同實(shí)現(xiàn)的。
- 創(chuàng)建一個(gè)實(shí)現(xiàn) InvocationHandler 接口的代理類,并在其中實(shí)現(xiàn) invoke 方法。在 invoke 方法中,可以根據(jù)需要執(zhí)行額外的邏輯,然后調(diào)用目標(biāo)對(duì)象的方法。
- 使用 Proxy 類的靜態(tài)方法 newProxyInstance 創(chuàng)建代理對(duì)象。該方法接收三個(gè)參數(shù):ClassLoader、要實(shí)現(xiàn)的接口數(shù)組以及 InvocationHandler 對(duì)象。
- 通過(guò)代理對(duì)象調(diào)用方法。
4. JDK 動(dòng)態(tài)代理的使用示例
// 定義接口 public interface UserService { void addUser(String username); } // 實(shí)現(xiàn)接口 public class UserServiceImpl implements UserService { @Override public void addUser(String username) { System.out.println("添加用戶:" + username); } } // 創(chuàng)建代理類 public class LogProxy implements InvocationHandler { private Object target; public LogProxy(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("開始記錄日志"); Object result = method.invoke(target, args); System.out.println("結(jié)束記錄日志"); return result; } } // 使用代理對(duì)象 public class Main { public static void main(String[] args) { UserService userService = new UserServiceImpl(); LogProxy logProxy = new LogProxy(userService); UserService proxy = (UserService) Proxy.newProxyInstance( userService.getClass().getClassLoader(), userService.getClass().getInterfaces(), logProxy); proxy.addUser("Alice"); } }
輸出結(jié)果:
開始記錄日志
添加用戶:Alice
結(jié)束記錄日志
5. JDK 動(dòng)態(tài)代理的優(yōu)點(diǎn)
- 簡(jiǎn)化了代碼結(jié)構(gòu),將通用邏輯與業(yè)務(wù)邏輯分離。
- 可以在運(yùn)行時(shí)動(dòng)態(tài)地創(chuàng)建代理對(duì)象,無(wú)需提前編寫代理類。
- 支持對(duì)接口的代理,可以實(shí)現(xiàn)多個(gè)接口的代理。
6. JDK 動(dòng)態(tài)代理的缺點(diǎn)
- 只能代理接口,無(wú)法代理具體類。
- 被代理的目標(biāo)對(duì)象必須實(shí)現(xiàn)至少一個(gè)接口。
- 動(dòng)態(tài)代理的性能相對(duì)較低,因?yàn)樯婕暗椒瓷洳僮鳌?/li>
7. JDK 動(dòng)態(tài)代理的使用注意事項(xiàng)
- 接口方法中不能使用 final 修飾符,否則會(huì)拋出異常。
- 在 invoke 方法中需要判斷是否調(diào)用了 Object 類的方法,避免死循環(huán)。
- 如果目標(biāo)對(duì)象是通過(guò) Spring 容器管理的,建議使用 Spring AOP 來(lái)實(shí)現(xiàn)代理,而不是手動(dòng)使用 JDK 動(dòng)態(tài)代理。
8. 總結(jié)
JDK 動(dòng)態(tài)代理是 Java 中一種實(shí)現(xiàn)代理模式的機(jī)制,它基于 Java 的反射機(jī)制實(shí)現(xiàn)。使用 JDK 動(dòng)態(tài)代理可以實(shí)現(xiàn)橫切關(guān)注點(diǎn)的分離和解耦合,提高代碼的可維護(hù)性和復(fù)用性。然而,JDK 動(dòng)態(tài)代理只能代理接口,被代理的目標(biāo)對(duì)象必須實(shí)現(xiàn)至少一個(gè)接口,并且性能相對(duì)較低。在使用時(shí)需要注意一些細(xì)節(jié),如不能使用 final 修飾符、避免死循環(huán)等。
以上就是JDK動(dòng)態(tài)代理提高代碼可維護(hù)性和復(fù)用性利器的詳細(xì)內(nèi)容,更多關(guān)于JDK動(dòng)態(tài)代理的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- 深入了解SpringAOP中的jdk動(dòng)態(tài)代理與CGlib
- Java兩種動(dòng)態(tài)代理JDK動(dòng)態(tài)代理和CGLIB動(dòng)態(tài)代理詳解
- Java的動(dòng)態(tài)代理模式之JDK代理詳解
- Spring之AOP兩種代理機(jī)制對(duì)比分析(JDK和CGLib動(dòng)態(tài)代理)
- Java JDK與cglib動(dòng)態(tài)代理有什么區(qū)別
- 解讀jdk動(dòng)態(tài)代理為什么必須實(shí)現(xiàn)接口
- Java實(shí)現(xiàn)JDK動(dòng)態(tài)代理的原理詳解
- Java反射(JDK)與動(dòng)態(tài)代理(CGLIB)詳解
- Java JDK動(dòng)態(tài)代理在攔截器和聲明式接口中的應(yīng)用小結(jié)
相關(guān)文章
JavaWeb Session失效時(shí)間設(shè)置方法
這篇文章主要介紹了JavaWeb Session失效時(shí)間設(shè)置方法,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-12-12多線程Thread,Runnable,Callable實(shí)現(xiàn)方式
這篇文章主要為大家詳細(xì)介紹了Java多線程如何實(shí)現(xiàn)Thread,Runnable,Callable的方式,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-08-08Mybatis-plus支持Gbase8s分頁(yè)的實(shí)現(xiàn)示例
本文主要介紹了Mybatis-plus支持Gbase8s分頁(yè)的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-11-11java數(shù)據(jù)結(jié)構(gòu)排序算法之歸并排序詳解
這篇文章主要介紹了java數(shù)據(jù)結(jié)構(gòu)排序算法之歸并排序,結(jié)合具體實(shí)例形式詳細(xì)分析了歸并排序的原理、實(shí)現(xiàn)技巧與相關(guān)注意事項(xiàng),需要的朋友可以參考下2017-05-05JS實(shí)現(xiàn)冒泡排序,插入排序和快速排序并排序輸出
這篇文章主要介紹了JS實(shí)現(xiàn)冒泡排序,插入排序和快速排序并從input文本框中獲取內(nèi)容進(jìn)行排序輸出,需要的朋友可以參考下2015-07-07Spring security自定義用戶認(rèn)證流程詳解
這篇文章主要介紹了Spring security自定義用戶認(rèn)證流程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03Java實(shí)現(xiàn)選擇排序算法的實(shí)例教程
這篇文章主要介紹了Java實(shí)現(xiàn)選擇排序算法的實(shí)例教程,選擇排序的時(shí)間復(fù)雜度為О(n²),需要的朋友可以參考下2016-05-05