-
代理模式簡介分類
-
概念
代理,是為了在不修改目標物件的基礎上,增強目標方法的業務邏輯。
客戶類需要執行的是目標物件的目標方法,但是真正執行的是代理物件的代理方法,客戶類對目標物件的訪問是通過代理物件來實現的。當然,代理類與目標類需要實現同一個介面。
-
舉例
生活中遇到了官司,我們平常老百姓對法律的瞭解不全面,所以一般都會請律師處理。
目標物件:法庭上我們一般稱為當事人即目標物件
代理類:律師稱為代理律師即代理類
共同介面:都為了一個共同的目標努力贏得官司即為共同介面
目標方法:我們所做的提供證據各種努力成為目標方法
代理方法:再此過程中我們可能做不全面,律師對證據材料等進行整理收集,結合法律法規進行辯證等等,此過程稱為代理方法
-
代理分類
代理模式一般分為靜態代理與動態代理,動態代理又分為JDK動態代理與CGLIB動態代理
-
-
靜態代理
-
概念
靜態代理是指,代理類在程式執行前就已經定義好,其與目標類等關係在程式執行前就已經確立。
靜態代理類似於富翁於私人律師的代理關係,並不是在發生官司之後才去請律師,而是在此之前已經確立好的代理關係。
-
實現與解析
a、定義業務介面
package com.rangers.proxy.staticProxy; /** * @Author Rangers * @Description * @Date 2021-03-09 **/ public interface IAccountService { // 轉賬業務介面 void transfer(); }
b、定義目標類與目標方法
package com.rangers.proxy.staticProxy; /** * @Author Rangers * @Description * @Date 2021-03-09 **/ public class AccountServiceImpl implements IAccountService { // 轉賬業務實現即目標方法 @Override public void transfer() { System.out.println("進行轉賬操作"); } }
c、定義代理類AccountServiceImplProxy,實現IAccountService介面。在有參構造方法中傳入目標物件,將目標物件引入代理類,以便代理類呼叫目標方法,進行增強
package com.rangers.proxy.staticProxy; /** * @Author Rangers * @Description * @Date 2021-03-09 **/ public class AccountServiceImplProxy implements IAccountService { // 宣告目標介面物件 private IAccountService target; public AccountServiceImplProxy() { } // 業務介面物件作為構造器,用於接收目標物件 public AccountServiceImplProxy(IAccountService target) { this.target = target; } @Override public void transfer() { // 此處對目標方法進行增強 System.out.println("對轉賬人身份校驗。。"); target.transfer(); System.out.println("進行日誌記錄。。"); } }
d、編寫測試類TransferServiceTest
package com.rangers.proxy.staticProxy; /** * @Author Rangers * @Description * @Date 2021-03-09 **/ public class TransferServiceTest { public static void main(String[] args) { // 建立目標物件 IAccountService target = new AccountServiceImpl(); // 建立代理物件,傳入目標物件進行初始化 IAccountService proxy = new AccountServiceImplProxy(target); // 執行代理物件的方法 proxy.transfer(); } }
-
-
動態代理
動態代理,程式在整個執行過程中根本就不存在目標類的代理類,目標物件的代理物件只是由代理工具在程式執行時由JVM根據反射等機制動態生成。代理物件與目標物件的代理關係在程式執行時才確立。
動態代理類似於普通人在有官司之後,再聘請律師的,即代理關係是在官司發生後確立的。
動態代理的實現方式有兩種:JDK的動態代理、CGLIB動態代理
a、JDK動態代理
-
概念
JDK動態代理是通過JDK提供的 java.lang.reflect.Proxy類實現動態大力,使用其靜態方法newProxyInstance(),對目標物件、業務介面及業務增強邏輯,自動生成一個動態代理物件。
public static newProxyInstance(ClassLoader classLoader,Class<?> interfaces,InvocationHandler handler) classLoader:傳入目標類的類載入器,通過目標物件的反射獲取 interfaces:目標物件實現的介面陣列,通過目標物件的反射獲取 handler:業務增強邏輯,需要具體實現
InvocationHandler是個介面,實現InvocationHandler介面的類用於增加目標類的業務邏輯。需要實現invoke()方法,具體的增強邏輯就是在此方法中進行實現,程式呼叫住業務邏輯時會自動呼叫invoke()方法
public Object invoke(Object proxy,Method method,Object[] args) proxy:生成的代理物件 method:目標方法 args:目標方法的引數
Method類物件,invoke()方法進行執行目標物件的目標方法
public Object invoke(Object obj,Object args) method.invoke(Object target,Object...args)執行目標方法 target:目標物件 args:目標方法的執行引數
-
實現與解析
- 定義業務介面與實現類
package com.rangers.proxy.jdkProxy; /** * @Author Rangers * @Description * @Date 2021-03-09 **/ public interface IAccountService { // 轉賬業務介面 void transfer(); }
package com.rangers.proxy.jdkProxy; /** * @Author Rangers * @Description * @Date 2021-03-09 **/ public class AccountServiceImpl implements IAccountService { // 轉賬業務實現即目標方法 @Override public void transfer() { System.out.println("進行轉賬操作"); } }
- 定義JdkProxy類實現InvocationHandler介面,實現invoke()方法,並對業務邏輯進行增強
package com.rangers.proxy.jdkProxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; /** * @Author Rangers * @Description * @Date 2021-03-09 **/ public class JdkProxy implements InvocationHandler { private Object target; public JdkProxy() { } public JdkProxy(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("進行日誌記錄。。"); return result; } }
- 新建測試類JDKProxyTest
package com.rangers.proxy.jdkProxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * @Author Rangers * @Description * @Date 2021-03-09 **/ public class JDKProxyTest { public static void main(String[] args) { // 建立目標物件 IAccountService target = new AccountServiceImpl(); // 建立代理物件,傳入目標物件進行初始化 IAccountService proxyService = (IAccountService) Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), new JdkProxy(target) ); // 亦可使用匿名類進行實現 /*(IAccountService) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 此處對目標方法進行增強 System.out.println("對轉賬人身份校驗。。"); Object result = method.invoke(target, args); System.out.println("進行日誌記錄。。"); return result; } });*/ // 此處執行的業務方法就是代理物件的增強過的邏輯 proxyService.transfer(); } }
注:使用JDK動態代理時需要目標類目標方法必須在實現的介面中,否則不能使用此方式進行動態打擊。對於無介面的類需要實現動態代理,就要使用CGLIB方式來進行實現
b、CGLIB動態代理
-
概念
CGLIB是一個開源的第三方程式碼生成類庫,對於無介面的類,要為其建立動態代理,就要使用CGLIB進行實現。CGLIB代理的生成原理是生成目標類的子類,子類是增強過的,就是目標類的代理類。所以,使用CGLIB生成動態代理,要求目標類必須能夠被繼承,即不能是final修飾的類。
CGLIB包的底層是通過使用一個小兒快的位元組碼處理框架ASM(java位元組碼操控框架),來轉換位元組碼並生成新的類,通過對位元組碼進行增強來生成代理類。
我們靜態代理理解為私人律師,JDK動態代理成為代理律師,CGLIB動態代理可以理解為老父親的兒子。老父親是被需要增強對目標類,兒子則是用於增強父親對代理類,事先不需要約定。父親需要兒子增強什麼,兒子就增強什麼,即他們之間的關係不要介面來進行約束。
-
注意要點
使用CGLIB動態代理時,生成代理類的類需要實現MethodInterceptor介面及intercept()方法
public Object intercept(Object proxy,Method method,Objectp[] args,MethodProxy methodProxy) proxy:代理物件 method:代理物件的方法,即增強後的方法 args:方法引數 methodProxy:代理方法的物件
建立代理物件時使用Enhancer類
// 建立增強器 Enhancer enhancer = new Enhancer(); // 初始化增強器:將目標類指定為父類 enhancer.setSuperclass(target.class); // 初始化增強器:設定回撥至本類中的intercept()方法 enhancer.setCallback(this); // 使用增強器建立代理物件進行返回 enhancer.create();
-
實現與解析
-
引入CGLIB依賴
<dependency> <groupId>cglib</groupId> <artifactId>cglib-full</artifactId> <version>2.0.2</version> </dependency>
-
建立目標類AccountService
package com.rangers.proxy.cglibProxy; /** * @Author Rangers * @Description * @Date 2021-03-09 **/ public class AccountService { // 轉賬業務 即目標方法 public void transfer() { System.out.println("進行轉賬操作"); } // 查詢餘額 即目標方法 public void getBalance() { System.out.println("查詢餘額操作"); } }
-
建立代理類AccountServiceCglibProxy實現MethodInterceptor介面,完善intercept()方法進行增強,建立生成代理物件createProxy()方法
package com.rangers.proxy.cglibProxy; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; /** * @Author Rangers * @Description * @Date 2021-03-09 **/ public class AccountServiceCglibProxy implements MethodInterceptor { // 宣告目標類的成員變數,並建立以目標類為引數的構造器,用於接收目標物件 private AccountService target; public AccountServiceCglibProxy(AccountService accountService) { this.target = accountService; } @Override public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { // 此處對目標方法進行增強 Object result = new Object(); if ("transfer".equals(method.getName())){ System.out.println("對轉賬人身份校驗。。"); result = method.invoke(target, args); System.out.println("進行日誌記錄。。"); }else{ // 直接執行目標物件的目標方法 result = methodProxy.invokeSuper(target,args); } return result; } // 建立代理物件 public AccountService createProxy(){ // 建立增強器 Enhancer enhancer = new Enhancer(); // 初始化增強器:將目標類指定為父類 enhancer.setSuperclass(AccountService.class); // 初始化增強器:設定回撥至本類中的intercept()方法 enhancer.setCallback(this); // 使用增強器建立代理物件 return (AccountService) enhancer.create(); } }
-
建立測試類CglibProxyTest
package com.rangers.proxy.cglibProxy; /** * @Author Rangers * @Description * @Date 2021-03-09 **/ public class CglibProxyTest { public static void main(String[] args) { // 目標物件 AccountService target = new AccountService(); // 建立代理物件,傳入目標物件進行初始化 AccountService accountService = new AccountServiceCglibProxy(target).createProxy(); accountService.transfer(); accountService.getBalance(); } }
-
-
代理模式詳解:靜態代理、JDK動態代理與Cglib動態代理
相關文章
- 靜態代理和動態代理(jdk/cglib)詳解JDKCGLib
- Java代理(jdk靜態代理、動態代理和cglib動態代理)JavaJDKCGLib
- JDK動態代理和CGLib代理JDKCGLib
- JDK動態代理和 CGLIB 代理JDKCGLib
- 3.靜態代理&動態代理&CGlibCGLib
- spring aop原理 JDK動態代理和CGLIB動態代理SpringJDKCGLib
- 深入理解靜態代理與JDK動態代理JDK
- cglib動態代理和jdk動態代理的區別與應用CGLibJDK
- 你必須會的 JDK 動態代理和 CGLIB 動態代理JDKCGLib
- 靜態代理和動態代理
- JAVA 靜態代理 & 動態代理Java
- JDK動態代理詳解JDK
- Java動態代理(JDK和cglib)JavaJDKCGLib
- CGLib 動態代理CGLib
- 靜態代理、動態代理與Mybatis的理解MyBatis
- Spring動態代理的生成-如何判斷是使用JDK動態代理還是CGlib代理SpringJDKCGLib
- 代理模式 - 動態代理模式
- 由《尋秦記》說代理模式(靜態,動態,CGLib)模式CGLib
- 《Proxy系列專題》:代理模式(靜態、JDK、CGLib)模式JDKCGLib
- 動態代理-cglib分析CGLib
- AOP之靜態代理VS動態代理
- 動態代理jdk的Proxy與spring的CGlibJDKSpringCGLib
- 【設計模式】-代理模式及動態代理詳解設計模式
- JDK動態代理JDK
- 設計模式之cglib動態代理設計模式CGLib
- 動態代理jdk和cglib的區別JDKCGLib
- Java中的靜態代理和動態代理Java
- 輕鬆理解 Java 靜態代理/動態代理Java
- Java JDK Proxy和CGLib動態代理示例講解JavaJDKCGLib
- jdk的動態代理和靜態代理你還寫不出來嘛???JDK
- 代理模式-靜態代理解讀模式
- Java基礎系列-靜態代理和動態代理Java
- JDK動態代理初探JDK
- Spring原始碼剖析5:JDK和cglib動態代理原理詳解Spring原始碼JDKCGLib
- 深挖JDK動態代理(一)JDK
- Java靜態代理模式Java模式
- 20201207——靜態代理模式模式
- SpringAOP中JDK和CGLib動態代理哪個更快?SpringJDKCGLib