Java中的三種代理模式詳解
Java代理模式
什么是代理模式?
代理(Proxy)是一種設(shè)計(jì)模式,為其他對(duì)象提供一種代理以控制對(duì)這個(gè)對(duì)象的訪問(wèn)。
代理模式的組成
- 抽象角色:通過(guò)接口或抽象類聲明真實(shí)角色實(shí)現(xiàn)的業(yè)務(wù)方法。
- 代理角色:實(shí)現(xiàn)抽象角色,是真實(shí)角色的代理,通過(guò)真實(shí)角色的業(yè)務(wù)邏輯方法來(lái)實(shí)現(xiàn)抽象方法,并可以附加自己的操作。
- 真實(shí)角色:實(shí)現(xiàn)抽象角色,定義真實(shí)角色所要實(shí)現(xiàn)的業(yè)務(wù)邏輯,供代理角色調(diào)用。
代理模式的優(yōu)點(diǎn)
(1).職責(zé)清晰
真實(shí)的角色就是實(shí)現(xiàn)實(shí)際的[業(yè)務(wù)邏輯,不用關(guān)心其他非本職責(zé)的事務(wù),通過(guò)后期的代理完成一件完成事務(wù),附帶的結(jié)果就是編程簡(jiǎn)潔清晰。
(2).代理對(duì)象可以在客戶端和目標(biāo)對(duì)象之間起到中介的作用,這樣起到了中介的作用和保護(hù)了目標(biāo)對(duì)象的作用。
(3).高擴(kuò)展性
代理模式的關(guān)鍵點(diǎn)是:代理對(duì)象與目標(biāo)對(duì)象.代理對(duì)象是對(duì)目標(biāo)對(duì)象的擴(kuò)展,并會(huì)調(diào)用目標(biāo)對(duì)象。
1、靜態(tài)代理
? 靜態(tài)代理在使用時(shí),需要定義接口或者父類,被代理對(duì)象與代理對(duì)象一起實(shí)現(xiàn)相同的接口或者是繼承相同父類。
步驟1:創(chuàng)建UserDao.java接口
步驟2:創(chuàng)建UserDaoImpl.java(UserDao接口實(shí)現(xiàn)類)
步驟3:創(chuàng)建代理類Proxy01.java(實(shí)現(xiàn)UserDao接口)
/** * 代理對(duì)象,靜態(tài)代理 * */ public class Proxy01 implements UserDao { //接受保存目標(biāo)對(duì)象 UserDao target; public Proxy01() { } public Proxy01(UserDao target) { this.target = target; } @Override public void getUser() { System.out.println("事務(wù)開始"); target.getUser(); System.out.println("事務(wù)結(jié)束"); } }
步驟4:測(cè)試Proxy01_test.java
/** * 測(cè)試靜態(tài)代理 * 靜態(tài)代理:修改被代理類中的代理方法,需要修改代理類中的方法 * 缺點(diǎn): * * 因?yàn)榇韺?duì)象需要與目標(biāo)對(duì)象實(shí)現(xiàn)一樣的接口,所以會(huì)有很多代理類,類太多.同時(shí),一旦接口增加方法,目標(biāo)對(duì)象與代理對(duì)象都要維護(hù). * */ public class Proxy01_test { public static void main(String[] args) { //目標(biāo)對(duì)象 UserDao target = new UserDaoImpl(); //代理對(duì)象,把目標(biāo)對(duì)象傳給代理對(duì)象,建立代理關(guān)系 Proxy01 proxy = new Proxy01(target); proxy.getUser();//執(zhí)行的是代理的方法 } }
執(zhí)行結(jié)果:
總結(jié):
- 靜態(tài)代理在使用時(shí),需要定義接口或者父類,被代理對(duì)象與代理對(duì)象一起實(shí)現(xiàn)接口或繼承相同 的父類
缺點(diǎn):
- 因?yàn)榇韺?duì)象需要和被代理對(duì)象實(shí)現(xiàn)相同的接口或父類,所以會(huì)有太多的代理類
- 一旦接口中增加了方法后,被代理對(duì)象和代理對(duì)象都需要維護(hù)(非常麻煩,不方便)
2、jdk動(dòng)態(tài)代理
JDK中動(dòng)態(tài)代理類只需要使用java.lang.reflect.Proxy.newProxyInstance方法,該方法需要接收三個(gè)參數(shù),完整的寫法是:
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )
注意該方法是在Proxy類中是靜態(tài)方法,且接收的三個(gè)參數(shù)依次為:
- ClassLoader loader:指定當(dāng)前目標(biāo)對(duì)象使用類加載器,獲取加載器的方法是固定的
- Class<?>[] interfaces:目標(biāo)對(duì)象實(shí)現(xiàn)的接口類型,使用泛型方式確認(rèn)類型,獲取接口類型的方法是固定的
- InvocationHandler h;事件處理,執(zhí)行目標(biāo)對(duì)象的方法時(shí),會(huì)觸發(fā)事件處理器的方法,會(huì)把當(dāng)前執(zhí)行目標(biāo)對(duì)象的方法作為參數(shù)傳入
步驟1:創(chuàng)建UserDao.java接口
步驟2:創(chuàng)建UserDaoImpl.java(UserDao接口實(shí)現(xiàn)類)
步驟3:創(chuàng)建代理類Proxy02.java
/** * jdk動(dòng)態(tài)代理類 * */ public class Proxy02 { //維護(hù)一個(gè)目標(biāo)對(duì)象 private Object target; public Proxy02(Object target) { this.target = target; } //給目標(biāo)對(duì)象生成代理對(duì)象 public Object getProxyInstance(){ return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this::invoke); } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("開始事務(wù)2"); //執(zhí)行目標(biāo)對(duì)象方法 Object returnValue = method.invoke(target, args); System.out.println("提交事務(wù)2"); return returnValue; } }
步驟4:測(cè)試Proxy02_test.java
/** * 測(cè)試jdk動(dòng)態(tài)代理 * 代理對(duì)象不需要實(shí)現(xiàn)接口,但是目標(biāo)對(duì)象一定要實(shí)現(xiàn)接口,否則不能用動(dòng)態(tài)代理 * 接口中增加方法時(shí)只用在實(shí)現(xiàn)類中增加方法,不用修改代理類。 * */ public class Proxy02_test { public static void main(String[] args) { //目標(biāo)對(duì)象 UserDao target = new UserDaoImpl(); // 【原始的類型 class top.polygon.proxy01.dao.impl.UserDaoImpl】 System.out.println(target.getClass()); // 給目標(biāo)對(duì)象,創(chuàng)建代理對(duì)象 UserDao proxy = (UserDao) new Proxy02(target).getProxyInstance(); // class $Proxy0 內(nèi)存中動(dòng)態(tài)生成的代理對(duì)象 System.out.println(proxy.getClass()); proxy.getUser();//執(zhí)行的是代理的方法 proxy.insertUser(); } }
運(yùn)行結(jié)果:
總結(jié):
代理對(duì)象不需要實(shí)現(xiàn)接口,但是目標(biāo)對(duì)象一定要實(shí)現(xiàn)接口,否則不能用動(dòng)態(tài)代理
3、cglib動(dòng)態(tài)代理
靜態(tài)代理和jdk動(dòng)態(tài)代理都要求目標(biāo)對(duì)象實(shí)現(xiàn)接口,
cglib動(dòng)態(tài)代理,使用以目標(biāo)對(duì)象子類的方式實(shí)現(xiàn)代理,(當(dāng)目標(biāo)對(duì)象是一個(gè)單獨(dú)的對(duì)象,沒(méi)有實(shí)現(xiàn)任何接口時(shí)使用).
Cglib代理,也叫作子類代理,它是在內(nèi)存中構(gòu)建一個(gè)子類對(duì)象從而實(shí)現(xiàn)對(duì)目標(biāo)對(duì)象功能的擴(kuò)展。Cglib是一個(gè)強(qiáng)大的高性能的代碼生成包,它可以在運(yùn)行期擴(kuò)展Java類與實(shí)現(xiàn)Java接口。它廣泛的被許多AOP的框架使用,為其提供方法的interception(攔截),例如大家所熟知的Spring AOP。
? Cglib包的底層是通過(guò)使用一個(gè)小而快的字節(jié)碼處理框架ASM來(lái)轉(zhuǎn)換字節(jié)碼并生成新的類。
Cglib子類代理需要注意的是:
- 需要引入cglib的jar包
- 代理的類不能是final,否則報(bào)錯(cuò)
- 目標(biāo)對(duì)象的方法如果有final/static,那么不會(huì)被攔截,即不會(huì)執(zhí)行目標(biāo)對(duì)象額外的業(yè)務(wù)方法。
實(shí)現(xiàn)步驟:
步驟1:引入jar包
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.3.0</version> </dependency>
步驟2:創(chuàng)建被代理的類UserDaoImpl.java
public class UserDaoImpl{ public void getUser() { System.out.println("cglib動(dòng)態(tài)代理:得到用戶信息"); } public void insertUser() { System.out.println("cglib動(dòng)態(tài)代理:添加用戶"); } }
步驟3:創(chuàng)建代理類Proxy03.java
/** * cglib動(dòng)態(tài)代理 * * */ public class Proxy03 implements MethodInterceptor { //維護(hù)目標(biāo)對(duì)象 private Object target; public Proxy03(Object target) { this.target = target; } //給目標(biāo)對(duì)象創(chuàng)建一個(gè)代理對(duì)象 public Object getProxyInstance(){ //1.工具類 Enhancer en = new Enhancer(); //2.設(shè)置父類 en.setSuperclass(target.getClass()); //3.設(shè)置回調(diào)函數(shù) en.setCallback(this); //4.創(chuàng)建子類(代理對(duì)象) return en.create(); } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("開始事務(wù)..."); //執(zhí)行目標(biāo)對(duì)象的方法 Object returnValue = method.invoke(target, objects); System.out.println("提交事務(wù)..."); return returnValue; } }
步驟4:測(cè)試Proxy03_test.java
public class Proxy03_test { public static void main(String[] args) { //目標(biāo)對(duì)象 UserDao target = new UserDaoImpl(); System.out.println(target.getClass()); // 給目標(biāo)對(duì)象,創(chuàng)建代理對(duì)象 UserDao proxy = (UserDao) new Proxy03(target).getProxyInstance(); System.out.println(proxy.getClass()); proxy.getUser();//執(zhí)行的是代理的方法 proxy.insertUser(); } }
運(yùn)行結(jié)果
到此這篇關(guān)于Java中的三種代理模式詳解的文章就介紹到這了,更多相關(guān)Java代理模式內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
spring事務(wù)@Transactional失效原因及解決辦法小結(jié)
今天就跟大家聊聊有關(guān)spring中@Transactional失效原因及解決辦法小結(jié),主要從三個(gè)方面考慮,具有一定的參考價(jià)值,感興趣的可以了解一下2023-08-08基于Java HttpClient和Htmlparser實(shí)現(xiàn)網(wǎng)絡(luò)爬蟲代碼
這篇文章主要介紹了基于Java HttpClient和Htmlparser實(shí)現(xiàn)網(wǎng)絡(luò)爬蟲代碼的相關(guān)資料,需要的朋友可以參考下2015-12-12Spring?Boot?項(xiàng)目中?JPA?語(yǔ)法的基本使用方法
這篇文章主要介紹了?Spring?Boot?項(xiàng)目中?JPA?語(yǔ)法的基本使用方法,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-10-10java開發(fā)SpringBoot參數(shù)校驗(yàn)過(guò)程示例教程
這篇文章主要為大家介紹了SpringBoot如何進(jìn)行參數(shù)校驗(yàn)的過(guò)程示例詳解教程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2021-10-10Java?自定義注解在登錄驗(yàn)證的應(yīng)用示例
本文主要介紹了Java?自定義注解在登錄驗(yàn)證的應(yīng)用示例,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-12-12Java 數(shù)據(jù)結(jié)構(gòu)進(jìn)階二叉樹題集上
二叉樹可以簡(jiǎn)單理解為對(duì)于一個(gè)節(jié)點(diǎn)來(lái)說(shuō),最多擁有一個(gè)上級(jí)節(jié)點(diǎn),同時(shí)最多具備左右兩個(gè)下級(jí)節(jié)點(diǎn)的數(shù)據(jù)結(jié)構(gòu)。本文將帶你通過(guò)實(shí)際題目來(lái)熟練掌握2022-04-04詳解IDEA多module項(xiàng)目maven依賴的一些說(shuō)明
這篇文章主要介紹了詳解IDEA多module項(xiàng)目maven依賴的一些說(shuō)明,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-10-10