CGLIB動態代理使用介紹

一、前言

  說到動態代理,開發者們第一時間想到的就是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>
  1. 定義目標類:UserService
public class UserService {
    public String getUserName(Long userId) {
        System.out.println("獲取用戶名..");
        return "user" + userId;
    }
}
  1. 實現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中。

  1. 使用
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中的代碼完全解耦!快去試試吧!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章