JAVA 動(dòng)態(tài)字節(jié)碼生成之 CGLIB

前言

與 jdk 自帶代理只能代理 實(shí)現(xiàn)了接口對(duì)象不同,cglib可以代理任意一類,當(dāng)然該類不能為 final 類型。本文主要研究一下 Cglib 是什么、如何一些基本使用、底層實(shí)現(xiàn)原理。

Generates dynamic subclasses to enable method interception.This class started as a substitute for the standard Dynamic Proxy support included with JDK 1.3, but one that allowed the proxies to extend a concrete base class, in addition to implementing interfaces.

Cglib 是什么

Cglib 是一個(gè)強(qiáng)大的 高性能的字節(jié)碼生成工具,它被廣泛的用于許多AOP框架中,為他們提供攔截方法,參考下圖:

1.gif
  • 最底層時(shí)字節(jié)碼,一次編譯,到處運(yùn)行的虛擬字節(jié)碼指令
  • ASM 直接操作字節(jié)碼的框架,需要對(duì)字節(jié)碼的結(jié)構(gòu)比較熟悉
  • Groovy BeanShell 說(shuō)明運(yùn)行在虛擬機(jī)上的不一定是Java語(yǔ)言生成的字節(jié)碼,這也是虛擬機(jī)當(dāng)初涉及的初衷,因?yàn)椴欧譃? java 語(yǔ)言規(guī)范 和 虛擬機(jī)規(guī)范 兩部分。java 代碼 是通過(guò)編譯器直接生成的 字節(jié)碼
  • Hibername Spring AOP 等 大量運(yùn)用 cglib 提供的反射功能
  • 最上層 具體 項(xiàng)目應(yīng)用

使用Cglib做代理

1、引入依賴最新依賴查詢

        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.3.0</version>
        </dependency>

2、被代理的類

public class Dao {

    public void query(Long id) {
        System.out.println("Query method is invoked");
    }

    public void update(String key) {
        System.out.println("Update method is invoked");
    }
}

3、代理類

public class Client {

    public static void main(String[] args) {
        
        DaoProxy proxy = new DaoProxy();
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Dao.class);
        enhancer.setCallback(proxy);

        Dao dao = (Dao) enhancer.create();
        dao.query(1111L);
        dao.update("hello");

    }
}

4、輸出:

Before method invoked,method name :query
Before method invoked,method name :toString
Before method invoked,method name :hashCode
After method invoked----------------
Query method is invoked
After method invoked----------------
Before method invoked,method name :update
Update method is invoked
After method invoked----------------

對(duì)不同方法分別做代理

那么問(wèn)題來(lái)了,如果我想對(duì)不同的方法分別做不同處理,該如何呢?

比如:query 做攔截輸出 query , update 攔截輸出 update

先看代碼改造如下:

增加 filter

public class DaoFilter implements CallbackFilter {

    @Override
    public int accept(Method method) {
        if ("query".equals(method.getName())) {
            return 0;
        }
        if ("update".equals(method.getName())) {
            return 1;
        }
        return 2;
    }
}

分別增加 對(duì)應(yīng)的 proxy 處理:

public class DaoForQueryProxy implements MethodInterceptor {

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("Method query filter is interceptored!!");
        proxy.invokeSuper(obj, args);
        System.out.println("Method query filter is interceptored over!!");
        return obj;
    }
}

public class DaoForUpdateProxy implements MethodInterceptor {

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("Method update filter is interceptored!!");
        proxy.invokeSuper(obj, args);
        System.out.println("Method update filter is interceptored over!!");
        return obj;
    }
}

客戶端調(diào)用變更:

public class Client {

    public static void main(String[] args) {

        DaoProxy proxy = new DaoProxy();
        DaoForQueryProxy queryProxy = new DaoForQueryProxy();
        DaoForUpdateProxy updateProxy = new DaoForUpdateProxy();

        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Dao.class);
        //enhancer.setCallback(proxy);
        enhancer.setCallbacks(new Callback[] { queryProxy, updateProxy, proxy, NoOp.INSTANCE });
        enhancer.setCallbackFilter(new DaoFilter());

        Dao dao = (Dao) enhancer.create();
        dao.query(1111L);
        dao.update("hello");

    }
}

用法解釋如下:

自定義一個(gè)filter 實(shí)現(xiàn) CallbackFilter,根據(jù)不同方法名稱 返回一個(gè) 從0 開(kāi)始的順序號(hào)。

針對(duì)不同的方法 分別實(shí)現(xiàn) 不同的 proxy。

enhancer.setCallbacks(new Callback[] { queryProxy, updateProxy, proxy, NoOp.INSTANCE });

set 的數(shù)組順序就代表了 需要跟 filter 中順序保持一致。

如上 就可以了,還有個(gè)設(shè)置 enhancer.setInterceptDuringConstruction(false);

作用就是如果攔截方法出現(xiàn)在構(gòu)造方法中不執(zhí)行輸出

源碼方面可以debug 自己跟進(jìn),涉及到底層 字節(jié)碼的一些生成

我相信喬布斯說(shuō)的,只有那些瘋狂到認(rèn)為自己可以改變世界的人才能真正地改變世界。

附:代理模式姐妹篇

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容