-
代理模式簡介分類
-
概念
代理,是為了在不修改目標對象的基礎上,增強目標方法的業務邏輯。
客戶類需要執行的是目標對象的目標方法,但是真正執行的是代理對象的代理方法,客戶類對目標對象的訪問是通過代理對象來實現的。當然,代理類與目標類需要實現同一個接口。
-
舉例
生活中遇到了官司,我們平常老百姓對法律的了解不全面,所以一般都會請律師處理。
目標對象:法庭上我們一般稱為當事人即目標對象
代理類:律師稱為代理律師即代理類
共同接口:都為了一個共同的目標努力贏得官司即為共同接口
目標方法:我們所做的提供證據各種努力成為目標方法
代理方法:再此過程中我們可能做不全面,律師對證據材料等進行整理收集,結合法律法規進行辯證等等,此過程稱為代理方法
-
代理分類
代理模式一般分為靜態代理與動態代理,動態代理又分為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(); } }
-
-