您好,登錄后才能下訂單哦!
今天小編給大家分享一下Java結構型模式之代理模式怎么實現的相關知識點,內容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。
在代理模式(Proxy Pattern)屬于結構型模式。在代理模式中,我們對一個對象提供一個代理對象,使用代理對象控制原對象的引用,目的是為了透明的控制對象訪問
Java中的代理按照代理類生成時機不同分為靜態代理和動態代理,靜態代理的代理類在編譯器就生成,而動態代理的代理類在Java運行時動態生成。動態代理又分為JDK代理和CGLib代理。
業務代碼
/** * 靜態代理 */ public interface Pay { void pay(); } //真實類 class Alipay implements Pay { @Override public void pay() { System.out.println("支付寶支付"); } } //代理類 class AlipayProxy implements Pay{ //組合真實對象 private final Alipay alipay = new Alipay(); @Override public void pay() { long startTime = System.currentTimeMillis(); alipay.pay(); System.out.println("執行了" + (System.currentTimeMillis()-startTime) + "毫秒"); //支付寶支付 執行了0毫秒 } }
測試代碼
public class Client { public static void main(String[] args) { new AlipayProxy().pay(); } }
優點
符合開閉原則
功能增強無需改動原業務代碼(解耦)
缺點
一個具體類就要產生一個代理類,可能會造成類爆炸
為了彌補靜態代理的缺點,引入了動態代理
1.JDK動態代理(利用Java提供的代理機制)
業務代碼
/** * JDK動態代理 */ public interface Pay { void pay(); } //真實類 class Alipay implements Pay { @Override public void pay() { System.out.println("支付寶支付"); } } class PayProxy { //組合真實對象 private Pay pay; public PayProxy(Pay pay) { this.pay = pay; } public Pay getProxy() { return (Pay) Proxy.newProxyInstance(getClass().getClassLoader(), pay.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { long startTime = System.currentTimeMillis(); Object result = method.invoke(pay, args); System.out.println("執行了" + (System.currentTimeMillis() - startTime) + "毫秒"); return result; } }); } }
測試代碼
public class Client { public static void main(String[] args) { PayProxy payProxy = new PayProxy(new Alipay()); Pay pay = payProxy.getProxy(); pay.pay(); //支付寶支付 執行了0毫秒 } }
我們通過arthas工具進行反編譯,可以找到真正的代理類$Proxy0
//代理對象 public final class $Proxy0 extends Proxy implements Pay { private static Method m3; public $Proxy0(InvocationHandler invocationHandler) { super(invocationHandler); } static { // 通過反射獲取名叫pay的menthod m3 = Class.forName("com.designpattern.structure.proxy.v2.Pay").getMethod("pay", new Class[0]); return; } public final void pay() { // h是invocationHandler對象 this.h.invoke(this, m3, null); return; } }
總結執行流程如下
測試代碼里執行了pay.pay()
根據多態的特性,執行的是代理類($Proxy0)中的pay方法
代理類($Proxy0)中的pay方法中執行了invocationHandler對象的invoke方法
invocationHandler對象的invoke方法就是業務代碼中傳入的匿名內部類中重寫的invoke方法
在重寫的invoke方法中通過反射調用真實對象alipay的pay方法
2.CGLib動態代理
JDK動態代理要求必須定義接口,如果沒有定義接口,就可以使用CGLib動態代理,CGLib為JDK的動態代理提供了很好的補充
首先引入cglib-3.3.0.jar與asm-9.0.jar
業務代碼
//真實對象 class Alipay implements Pay { @Override public void pay() { System.out.println("支付寶支付"); } } class AlipayProxy implements MethodInterceptor { //組合真實對象 private Alipay alipay = new Alipay(); public Alipay getProxy(){ Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(Alipay.class); //設置回調函數 enhancer.setCallback(this); //返回代理對象 return (Alipay) enhancer.create(); } @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { long startTime = System.currentTimeMillis(); Object result = method.invoke(alipay, args); System.out.println("執行了" + (System.currentTimeMillis() - startTime) + "毫秒"); return result; } }
測試代碼
public class Client { public static void main(String[] args) { Alipay proxy = new AlipayProxy().getProxy(); proxy.pay(); //支付寶支付 執行了0毫秒 } }
JDK代理要求必須定義接口,CGLib不用
CGLib的原理是動態生成被代理類的子類,所以類和方法都不能定義成final
CGLib代理速度>JDK代理速度的場景:JDK1.6之前、JDK1.6與JDK1.7進行大量調用,其余場景JDK代理速度更快(因此在有接口的情況下推薦使用JDK動態代理)
優點
保護真實對象,使用代理對象與客戶端交互
符合開閉原則
客戶端與真實對象之間解耦
缺點
代理類的創建,增加了系統復雜度
1.功能擴展:日志、監控、事務
2.控制管理:權限、限流
3.遠程代理:FeignClient、RMI
4.動態邏輯:mybatis mapper、jpa
5.延遲加載:虛代理
上文提到靜態代理是一個具體類產生一個代理類,可能會造成類爆炸,我們現在反觀動態代理則是一個接口產生一個代理類,也可能會造成類爆炸,所以這里給出一個較為通用的實現
業務代碼
//記錄執行的時間的通用類 public class TimeRecordProxy<T> { private final T target; public TimeRecordProxy(T target) { this.target = target; } @SuppressWarnings("unchecked") public T getProxy() { return (T) Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this::invoke); } private Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException { long startTime = System.currentTimeMillis(); Object result = method.invoke(target, args); System.out.println("執行了" + (System.currentTimeMillis()-startTime) + "毫秒"); return result; } }
測試代碼
public class Client { public static void main(String[] args) { TimeRecordProxy<Pay> timeRecordProxy = new TimeRecordProxy<>(new Alipay()); timeRecordProxy.getProxy().pay(); //支付寶支付 執行了0毫秒 } }
以上就是“Java結構型模式之代理模式怎么實現”這篇文章的所有內容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。