一、前言
說到動態代理,開發者們第一時間想到的就是JDK動態代理和cglib動態代理。瞭解Spring的同學應該知道,Spring AOP功能
的底層實現,就是使用的這兩種動態代理。
兩者區別
- JDK的動態代理機制只能代理實現了接口的類,而沒有實現接口的類就不能實現JDK的動態代理;
- cglib動態代理是針對類來實現代理的,它的原理是對指定的目標類生成一個子類,並覆蓋其中方法實現增強。使用cglib實現動態代理,完全不受代理類必須實現接口的限制。
- cglib底層採用ASM字節碼生成框架,使用字節碼技術生成代理類,比使用Java反射效率要高。
因爲cglib動態代理採用的是繼承,所以不能對final修飾的類進行代理。
二、使用場景
“代理”二字,從字面意思上來看,就是代替目標類做一些預處理的事情。常用來把一些通用的、與業務邏輯無關的邏輯放到代理類中處理。如:事務管理、參數校驗、統計接口訪問量、調用前後打印日誌等等。
三、主要組件
MethodInterceptor
:方法攔截器
在調用目標方法時,cglib會回調MethodInterceptor接口方法,來實現你自己的代理邏輯,類似於JDK中的InvocationHandler接口。
/**
* proxy:代理對象,CGLib動態生成的代理類實例
* method:目標對象的方法,上文中實體類所調用的被代理的方法引用
* args:目標對象方法的參數列表,參數值列表
* methodProxy:代理對象的方法,生成的代理類對方法的代理引用
*/
public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,
MethodProxy proxy) throws Throwable;
可以使用methodProxy.invokeSuper(obj,arg)來調用代理類實例上的proxy方法的父類方法,這樣比反射調用方法快!
Enhancer
:字節碼增強器
用來關聯目標類和代理處理邏輯類,並創建代理實例。
四、示例
需求:在進入方法前開啓事務,方法調用結束後關閉事務。
由於cglib是一個第三方的框架,不是JDK自帶的,所以要引入maven依賴。
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.6</version>
</dependency>
- 定義目標類:UserService
public class UserService {
public String getUserName(Long userId) {
System.out.println("獲取用戶名..");
return "user" + userId;
}
}
- 實現
MethodInterceptor
,定義事務攔截器
public class TransactionInterceptor implements MethodInterceptor {
Object target;
public TransactionInterceptor(Object target) {
this.target = target;
}
/**
* proxy:代理對象,CGLib動態生成的代理類實例
* method:目標對象的方法,上文中實體類所調用的被代理的方法引用
* args:目標對象方法的參數列表,參數值列表
* methodProxy:代理對象的方法,生成的代理類對方法的代理引用
*/
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("開啓事務..." + proxy.getClass().getSimpleName());
Object objValue = null;
try {
// 反射調用目標類方法
objValue = method.invoke(target, args);
System.out.println("返回值爲:" + objValue);
} catch (Exception e) {
System.out.println("調用異常!" + e.getMessage());
} finally {
System.out.println("調用結束,關閉事務...");
}
return objValue;
}
/**
* 獲取代理實例
*/
public Object getTargetProxy() {
// Enhancer類是cglib中的一個字節碼增強器,它可以方便的爲你所要處理的類進行擴展
Enhancer eh = new Enhancer();
// 1.將目標對象所在的類作爲Enhancer類的父類
eh.setSuperclass(target.getClass());
// 2.通過實現MethodInterceptor實現方法回調
eh.setCallback(this);
// 3. 創建代理實例
return eh.create();
}
}
一般把獲取代理實例的方法,也放在自定義的
MethodInterceptor
中。
- 使用
public static void main(String[] args) {
// 1. 創建目標實例
UserService userService = new UserService();
// 2. 創建事務攔截器
TransactionInterceptor transactionInterceptor = new TransactionInterceptor(userService);
// 3. 創建代理實例
UserService userServiceProxy = (UserService) transactionInterceptor.getTargetProxy();
// 4. 使用代理實例調用目標方法
userServiceProxy.getUserName(6L);
}
這樣便達到了代理的效果,開啓、關閉事務的代碼模塊化到自定義的MethodInterceptor
中,與UserService中的代碼完全解耦!快去試試吧!