最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

LambdaQueryWrapper的實(shí)現(xiàn)原理分析和lambda的序列化問題

 更新時(shí)間:2022年01月11日 08:46:35   作者:之誠  
這篇文章主要介紹了LambdaQueryWrapper的實(shí)現(xiàn)原理分析和lambda的序列化問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教。

LambdaQueryWrapper的實(shí)現(xiàn)原理

mybatis-plus的LambdaQueryWrapper的lambda來組合查詢字段的功能十分好用,但是它是如何實(shí)現(xiàn)的呢?

通過查看mybatis的源碼發(fā)現(xiàn)它的功能主要是四個(gè)類來實(shí)現(xiàn)的。

在這里插入圖片描述

我將其copy下來分析下。

SFunction 類

/**
 * 支持序列化的 Function
 *
 * @author miemie
 * @since 2018-05-12
 */
@FunctionalInterface
public interface SFunction<T, R> extends Function<T, R>, Serializable {
}

我們知道每個(gè)lambda表達(dá)式都有一個(gè)對(duì)應(yīng)的接口, 而mybatis-plus就是使用上面的接口來聲明lambda表達(dá)式的。 可以看到它實(shí)現(xiàn)了Serializable接口。

LambdaUtils

/**
 * Lambda 解析工具類
 *
 * @author HCL, MieMie
 * @since 2018-05-10
 */
public final class LambdaUtils {
.....................
 /**
     * 獲取對(duì)應(yīng)的表字段與對(duì)象的屬性關(guān)系對(duì)象
     *
     * @param func
     * @param <T>
     * @return
     */
    public static <T> EntityTableDefine.ColumnProp getColumnProp(SFunction<T, ?> func) {
        SerializedLambda resolve = LambdaUtils.resolve(func);
        return getColumnProp(resolve);
    }
  /**
     * 解析 lambda 表達(dá)式, 該方法只是調(diào)用了 {@link SerializedLambda#resolve(SFunction)} 中的方法,在此基礎(chǔ)上加了緩存。
     * 該緩存可能會(huì)在任意不定的時(shí)間被清除
     *
     * @param func 需要解析的 lambda 對(duì)象
     * @param <T>  類型,被調(diào)用的 Function 對(duì)象的目標(biāo)類型
     * @return 返回解析后的結(jié)果
     * @see SerializedLambda#resolve(SFunction)
     */
    public static <T> SerializedLambda resolve(SFunction<T, ?> func) {
        Class<?> clazz = func.getClass();
        return Optional.ofNullable(FUNC_CACHE.get(clazz))
                .map(WeakReference::get)
                .orElseGet(() -> {
                    SerializedLambda lambda = SerializedLambda.resolve(func);
                    FUNC_CACHE.put(clazz, new WeakReference<>(lambda));
                    return lambda;
                });
    }
  
  
  ................... 
}

把其中最重要的兩個(gè)方法貼出來,resolve 方法才是重點(diǎn)。 可以看到其中調(diào)用了SerializedLambda.resolve(func);方法。

SerializedLambda

/**
 * 這個(gè)類是從 {@link java.lang.invoke.SerializedLambda} 里面 copy 過來的,
 * 字段信息完全一樣
 * <p>負(fù)責(zé)將一個(gè)支持序列的 Function 序列化為 SerializedLambda</p>
 *
 * @author HCL
 * @since 2018/05/10
 */
@SuppressWarnings("unused")
public class SerializedLambda implements Serializable {
  ........
    
/**
     * 通過反序列化轉(zhuǎn)換 lambda 表達(dá)式,該方法只能序列化 lambda 表達(dá)式,不能序列化接口實(shí)現(xiàn)或者正常非 lambda 寫法的對(duì)象
     *
     * @param lambda lambda對(duì)象
     * @return 返回解析后的 SerializedLambda
     */
    public static SerializedLambda resolve(SFunction<?, ?> lambda) {
        if (!lambda.getClass().isSynthetic()) {
            throw ExceptionUtils.mpe("該方法僅能傳入 lambda 表達(dá)式產(chǎn)生的合成類");
        }
        try (ObjectInputStream objIn = new ObjectInputStream(new ByteArrayInputStream(SerializationUtils.serialize(lambda))) {
            /**
             * 實(shí)現(xiàn)反序列化的類型的替換, 使用我們自定義的類型來替換java.lang.invoke.SerializedLambda類。
             * 為何可以替換成功, 因?yàn)榉葱蛄谢臅r(shí)候使用的是反射的方式賦值的, 只要兩個(gè)類的方法名稱或者字段名一樣,反射調(diào)用是沒有問題的。
             * @param objectStreamClass
             * @return
             * @throws IOException
             * @throws ClassNotFoundException
             */
            @Override
            protected Class<?> resolveClass(ObjectStreamClass objectStreamClass) throws IOException, ClassNotFoundException {
                Class<?> clazz = super.resolveClass(objectStreamClass);
                return clazz == java.lang.invoke.SerializedLambda.class ? SerializedLambda.class : clazz;
            }
        }) {
            //因?yàn)榍懊娴奶鎿Q,這里獲取的就是我們自己定義的SerializedLambda類
            return (SerializedLambda) objIn.readObject();
        } catch (ClassNotFoundException | IOException e) {
            throw ExceptionUtils.mpe("This is impossible to happen", e);
        }
    }
  
  .............. 
    
 }

SerializationUtils.serialize(lambda)方法就是正常的序列化類, 無什么特別的.

resolveClass方法才是重點(diǎn)方法, 這個(gè)方法的目的是獲取反序列化后的類的類型,上面是被重新了。 參數(shù)ObjectStreamClass中是包含了反序列化后的類型,在jdk8之后lambda被反序列化后類型都是java.lang.invoke.SerializedLambda.class,這里重寫進(jìn)行了替換成自己定義的SerializedLambda類型。

兩個(gè)類型的代碼是一樣的(沒發(fā)現(xiàn)差異), mybatis-plus之所以復(fù)制這個(gè)類是為了方便控制吧(猜測(cè))。 SerializedLambda類中就包含了lambda的方法的名稱,而get/set方法的名稱自然就能對(duì)應(yīng)到具體的字段了。

至于為何可以替換的原因我在這個(gè)方法上面注釋了。

思考

序列化和反序列化是比價(jià)消耗性能的, 所以mybatis-plus使用了static的Map和WeakReference來緩存了序列化后的SerializedLambda對(duì)象。 至于為何使用WeakReference的方式來做緩存, 可以參考下ThreadLocal的實(shí)現(xiàn)原理

其實(shí)mybatis-plus的實(shí)現(xiàn)方式顯得繁瑣了。其實(shí)沒有必要去復(fù)制SerializedLambda類代碼,也沒有必要去真的序列化和反序列。

對(duì)象序列化中的 writeReplace 和 readResolve

  • writeReplace:在將對(duì)象序列化之前,如果對(duì)象的類或父類中存在writeReplace方法,則使用writeReplace的返回值作為真實(shí)被序列化的對(duì)象;writeReplace在writeObject之前執(zhí)行;
  • readResolve:在將對(duì)象反序列化之后,ObjectInputStream.readObject返回之前,如果從對(duì)象流中反序列化得到的對(duì)象所屬類或父類中存在readResolve方法,則使用readResolve的返回值作為ObjectInputStream.readObject的返回值;readResolve在readObject之后執(zhí)行;

函數(shù)式接口如果繼承了Serializable,使用Lambda表達(dá)式來傳遞函數(shù)式接口時(shí),編譯器會(huì)為Lambda表達(dá)式生成一個(gè)writeReplace方法,這個(gè)生成的writeReplace方法會(huì)返回java.lang.invoke.SerializedLambda;可以從反射Lambda表達(dá)式的Class證明writeReplace的存在(具體操作與截圖在后面);所以在序列化Lambda表達(dá)式時(shí),實(shí)際上寫入對(duì)象流中的是一個(gè)SerializedLambda對(duì)象,且這個(gè)對(duì)象包含了Lambda表達(dá)式的一些描述信息;

SerializedLambda類中有readResolve方法,這個(gè)readResolve方法中通過反射調(diào)用了Lambda表達(dá)式所在外部類中的** deserializeLambda deserializeLambda deserializeLambda**方法,這個(gè)方法是編譯器自動(dòng)生成的,可以通過反編譯.class字節(jié)碼證明(具體操作與截圖在后面); deserializeLambda deserializeLambda deserializeLambda方法內(nèi)部解析SerializedLambda,并調(diào)用LambdaMetafactory.altMetafactory或LambdaMetafactory.metafactory方法(引導(dǎo)方法)得到一個(gè)調(diào)用點(diǎn)(CallSite),CallSite會(huì)被動(dòng)態(tài)指定為Lambda表達(dá)式代表的函數(shù)式接口類型,并作為Lambda表達(dá)式返回;所以在從對(duì)象流反序列化得到SerializedLambda對(duì)象之后,又被轉(zhuǎn)換成原來的Lambda表達(dá)式,通過ObjectInputStream.readObject返回;

從上面的黑體中就能夠知道, 在序列化lambda的時(shí)候?qū)嶋H上是序列化了SerializedLambda對(duì)象,所以反序列化后就能獲取SerializedLambda對(duì)象了。 實(shí)際上序列化的對(duì)象是通過writeReplace方法產(chǎn)生的,那么我們要獲取SerializedLambda對(duì)象沒必要真的序列化和反序列化一遍。 反射調(diào)用writeReplace方法就可以了。

具體示例如下

package xyz.xiezc.ioc.starter.orm.lambda;
import cn.hutool.json.JSONUtil;
import lombok.Data;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Method;
@Data
public class LambdaTest {
    private String fieldA;
    public static void main(String[] args) throws Exception {
        SerializedLambda serializedLambda = doSFunction(LambdaTest::getFieldA);
        System.out.println("方法名:" + serializedLambda.getImplMethodName());
        System.out.println("類名:" + serializedLambda.getImplClass());
        System.out.println("serializedLambda:" + JSONUtil.toJsonStr(serializedLambda));
    }
    private static <T, R> java.lang.invoke.SerializedLambda doSFunction(SFunction<T, R> func) throws Exception {
        // 直接調(diào)用writeReplace
        Method writeReplace = func.getClass().getDeclaredMethod("writeReplace");
        writeReplace.setAccessible(true);
      	//反射調(diào)用
        Object sl = writeReplace.invoke(func);
        java.lang.invoke.SerializedLambda serializedLambda = (java.lang.invoke.SerializedLambda) sl;
        return serializedLambda;
    }
}

輸出結(jié)果: 可以看到獲取到了方法名和類名。 知道方法名再去掉get/set前綴就是字段名稱了

方法名:getFieldA
類名:xyz/xiezc/ioc/starter/orm/lambda/LambdaTest
serializedLambda:{"implMethodName":"getFieldA","implClass":"xyz/xiezc/ioc/starter/orm/lambda/LambdaTest","functionalInterfaceClass":"xyz/xiezc/ioc/starter/orm/lambda/SFunction","capturingClass":"xyz/xiezc/ioc/starter/orm/lambda/LambdaTest","instantiatedMethodType":"(Lxyz/xiezc/ioc/starter/orm/lambda/LambdaTest;)Ljava/lang/String;","functionalInterfaceMethodSignature":"(Ljava/lang/Object;)Ljava/lang/Object;","implMethodSignature":"()Ljava/lang/String;","functionalInterfaceMethodName":"apply","implMethodKind":5}

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • MyBatisPlus中事務(wù)處理的實(shí)現(xiàn)

    MyBatisPlus中事務(wù)處理的實(shí)現(xiàn)

    本文主要介紹了MyBatisPlus中事務(wù)處理的實(shí)現(xiàn),包括事務(wù)的開啟、提交、回滾等操作,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-07-07
  • 詳解Java是如何通過接口來創(chuàng)建代理并進(jìn)行http請(qǐng)求

    詳解Java是如何通過接口來創(chuàng)建代理并進(jìn)行http請(qǐng)求

    今天給大家?guī)淼闹R(shí)是關(guān)于Java的,文章圍繞Java是如何通過接口來創(chuàng)建代理并進(jìn)行http請(qǐng)求展開,文中有非常詳細(xì)的介紹及代碼示例,需要的朋友可以參考下
    2021-06-06
  • MyBatis框架搭建與代碼解讀分析

    MyBatis框架搭建與代碼解讀分析

    MyBatis是一個(gè)靈活的持久層框架,適合與數(shù)據(jù)庫交互,支持自定義SQL和高級(jí)映射,這篇文章給大家介紹MyBatis框架搭建與代碼解讀,感興趣的朋友跟隨小編一起看看吧
    2024-08-08
  • 全方位解析key值不確定的json數(shù)據(jù)

    全方位解析key值不確定的json數(shù)據(jù)

    這篇文章主要介紹了全方位解析key值不確定的json數(shù)據(jù),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2021-01-01
  • SpringBoot自定義Starter與自動(dòng)配置實(shí)現(xiàn)方法詳解

    SpringBoot自定義Starter與自動(dòng)配置實(shí)現(xiàn)方法詳解

    在Spring Boot官網(wǎng)為了簡化我們的開發(fā),已經(jīng)提供了非常多場(chǎng)景的Starter來為我們使用,即便如此,也無法全面的滿足我們實(shí)際工作中的開發(fā)場(chǎng)景,這時(shí)我們就需要自定義實(shí)現(xiàn)定制化的Starter
    2023-02-02
  • IDEA中sout快捷鍵無效問題的解決方法

    IDEA中sout快捷鍵無效問題的解決方法

    這篇文章主要介紹了IDEA中sout快捷鍵無效問題,在類文件中進(jìn)行操作會(huì)造成sout快捷命令無法自動(dòng)生成,比如操作了import引入其它包之后,本文給大家分享解決方法,感興趣的朋友一起看看吧
    2022-07-07
  • idea如何查看安裝插件的位置

    idea如何查看安裝插件的位置

    這篇文章主要介紹了idea如何查看安裝插件的位置問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-06-06
  • 淺談Java代碼的 微信長鏈轉(zhuǎn)短鏈接口使用 post 請(qǐng)求封裝Json(實(shí)例)

    淺談Java代碼的 微信長鏈轉(zhuǎn)短鏈接口使用 post 請(qǐng)求封裝Json(實(shí)例)

    下面小編就為大家?guī)硪黄獪\談Java代碼的 微信長鏈轉(zhuǎn)短鏈接口使用 post 請(qǐng)求封裝Json(實(shí)例)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-07-07
  • MyBatis?Plus實(shí)現(xiàn)中文排序的兩種有效方法

    MyBatis?Plus實(shí)現(xiàn)中文排序的兩種有效方法

    在MyBatis?Plus項(xiàng)目開發(fā)中,針對(duì)中文數(shù)據(jù)的排序需求是一個(gè)常見的挑戰(zhàn),尤其是在需要按照拼音或特定語言邏輯排序時(shí),本文整合了兩種有效的方法,旨在幫助開發(fā)者克服MyBatis?Plus在處理中文排序時(shí)遇到的障礙,需要的朋友可以參考下
    2024-08-08
  • 詳解springboot如何更新json串里面的內(nèi)容

    詳解springboot如何更新json串里面的內(nèi)容

    這篇文章主要為大家介紹了springboot 如何更新json串里面的內(nèi)容,文中有詳細(xì)的解決方案供大家參考,對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下
    2023-10-10

最新評(píng)論

汽车| 喜德县| 宿迁市| 瑞昌市| 牡丹江市| 舞钢市| 普定县| 门头沟区| 岳西县| 高陵县| 库车县| 阳朔县| 收藏| 临猗县| 高清| 南岸区| 余庆县| 辽源市| 泾源县| 延安市| 禄丰县| 股票| 句容市| 松江区| 涞水县| 永新县| 德惠市| 扶风县| 大同市| 德保县| 霸州市| 乳山市| 丹凤县| 土默特右旗| 闸北区| 天台县| 湖口县| 汪清县| 山东| 札达县| 巩义市|