日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當前位置:首頁 > 科技  > 軟件

通過方法引用獲取屬性名的底層邏輯是什么?

來源: 責編: 時間:2024-04-11 09:04:24 196觀看
導讀很多小伙伴可能都用過 MyBatis-Plus,這里邊我們構造 where 條件的時候,可以直接通過方法引用的方式去指定屬性名:LambdaQueryWrapper<Book> qw = new LambdaQueryWrapper<>();qw.eq(Book::getId, 2);List<Book> list = b

很多小伙伴可能都用過 MyBatis-Plus,這里邊我們構造 where 條件的時候,可以直接通過方法引用的方式去指定屬性名:4kt28資訊網——每日最新資訊28at.com

LambdaQueryWrapper<Book> qw = new LambdaQueryWrapper<>();qw.eq(Book::getId, 2);List<Book> list = bookMapper.selectList(qw);System.out.println("list = " + list);

Book::getId 這就是方法引用,松哥之前也專門寫過文章介紹相關內容,這里就不再多說。這里我們就單純來說說為什么 MP 通過 Book::getId 就可以識別出來這里的屬性名。4kt28資訊網——每日最新資訊28at.com

1. 源碼分析

這個問題其實好解決,我們順著 qw.eq 這個方法往下看就可以了,這個方法在執行的過程中幾經輾轉會來到 getColumnCache 方法中,這個方法就是解析出來屬性值的地方。4kt28資訊網——每日最新資訊28at.com

protected ColumnCache getColumnCache(SFunction<T, ?> column) {    LambdaMeta meta = LambdaUtils.extract(column);    String fieldName = PropertyNamer.methodToProperty(meta.getImplMethodName());    Class<?> instantiatedClass = meta.getInstantiatedClass();    tryInitCache(instantiatedClass);    return getColumnCache(fieldName, instantiatedClass);}

首先這里先將我們傳入的 Lambda 表達式通過 LambdaUtils.extract 方法解析出來一個 LambdaMeta 對象。4kt28資訊網——每日最新資訊28at.com

public static <T> LambdaMeta extract(SFunction<T, ?> func) {    // 1. IDEA 調試模式下 lambda 表達式是一個代理    if (func instanceof Proxy) {        return new IdeaProxyLambdaMeta((Proxy) func);    }    // 2. 反射讀取    try {        Method method = func.getClass().getDeclaredMethod("writeReplace");        method.setAccessible(true);        return new ReflectLambdaMeta((SerializedLambda) method.invoke(func), func.getClass().getClassLoader());    } catch (Throwable e) {        // 3. 反射失敗使用序列化的方式讀取        return new ShadowLambdaMeta(com.baomidou.mybatisplus.core.toolkit.support.SerializedLambda.extract(func));    }}

這塊的重點其實就在反射讀取這塊,這是從我們傳入的 Lambda 中找到了一個名為 writeReplace 的方法,并且通過反射執行了這個方法,然后將執行結果封裝為一個 ReflectLambdaMeta 對象返回。4kt28資訊網——每日最新資訊28at.com

接下來回到 getColumnCache 方法中,繼續通過 String fieldName = PropertyNamer.methodToProperty(meta.getImplMethodName()); 獲取到屬性名稱。4kt28資訊網——每日最新資訊28at.com

這里有一個 meta.getImplMethodName() 方法,這個方法的拿到的其實就是我們 Lambda 表達式中的方法名,也就是 getId,然后再通過 PropertyNamer.methodToProperty 對這個方法名進行處理,最終拿到屬性名:4kt28資訊網——每日最新資訊28at.com

public static String methodToProperty(String name) {  if (name.startsWith("is")) {    name = name.substring(2);  } else if (name.startsWith("get") || name.startsWith("set")) {    name = name.substring(3);  } else {    throw new ReflectionException(        "Error parsing property name '" + name + "'.  Didn't start with 'is', 'get' or 'set'.");  }  if (name.length() == 1 || name.length() > 1 && !Character.isUpperCase(name.charAt(1))) {    name = name.substring(0, 1).toLowerCase(Locale.ENGLISH) + name.substring(1);  }  return name;}

大家看到,這個解析的過程其實就是把方法名的前綴 get/set/is 這些去掉,然后剩余的字符串首字母小寫之后返回。4kt28資訊網——每日最新資訊28at.com

這就是我們傳入 Book::getId,最終能夠拿到 id 這個名稱的原因。4kt28資訊網——每日最新資訊28at.com

現在的問題變成了 writeReplace 方法究竟是個什么方法?4kt28資訊網——每日最新資訊28at.com

2. writeReplace

這個方法其實是系統底層自動生成的。我們可以將 Lambda 表達式在運行時生成的字節碼保存下來,然后進行反編譯,這樣就能夠看到 writeReplace 方法了。4kt28資訊網——每日最新資訊28at.com

如果需要將 Lambda 運行時生成的字節碼保存,需要在啟動參數中添加如下內容:4kt28資訊網——每日最新資訊28at.com

-Djdk.internal.lambda.dumpProxyClasses=/Users/sang/workspace/code/mp_demo/lambda/

等于號后面的部分是指定生成的字節碼的保存位置,大家可以根據自己的實際情況去配置。4kt28資訊網——每日最新資訊28at.com

以本文一開頭的 Lambda 表達式為例,最終生成的字節碼反編譯之后,內容如下:4kt28資訊網——每日最新資訊28at.com

final class MpDemo02ApplicationTests$$Lambda$1164 implements SFunction {    private MpDemo02ApplicationTests$$Lambda$1164() {    }    public Object apply(Object var1) {        return ((Book)var1).getId();    }    private final Object writeReplace() {        return new SerializedLambda(MpDemo02ApplicationTests.class, "com/baomidou/mybatisplus/core/toolkit/support/SFunction", "apply", "(Ljava/lang/Object;)Ljava/lang/Object;", 5, "org/javaboy/mp_demo02/model/Book", "getId", "()Ljava/lang/Integer;", "(Lorg/javaboy/mp_demo02/model/Book;)Ljava/lang/Object;", new Object[0]);    }}

大家可以看到,apply 方法實際上是重寫的接口的方法,在這個方法中將傳入的對象強轉為 Book 類型,然后調用其 getId 方法。4kt28資訊網——每日最新資訊28at.com

然后大家看到,反編譯之后多了一個 writeReplace 方法,這個方法的返回值是一個 SerializedLambda,這個 SerializedLambda 對象其實就是對 Lambda 表達式的描述。基本上每個參數都能做到見名知意,我這里說一下第七個參數,值是 getId,這個參數的變量名是 implMethodName,這就是我們 Lambda 表達式中給出來的變量名。這也是第一小節中,meta.getImplMethodName() 所獲取到的值。4kt28資訊網——每日最新資訊28at.com

這下就清楚了,為什么寫了 Book::getId 就能拿到屬性名了。4kt28資訊網——每日最新資訊28at.com

3. 擴展知識

有的小伙伴注意到,在 qw.eq(Book::getId, 2); 方法中,第一個參數是一個 SFunction 的實例,那就說我直接給一個 SFunction 的實例,不用 Lambda。大家注意,這種寫法不對!4kt28資訊網——每日最新資訊28at.com

原因在于經過前面的源碼分析之后,我們發現,MP 中根據 Book::getId 去獲取屬性名稱,一個關鍵點是利用 Lambda 在執行的時候生成的字節碼去獲取,如果你都沒有用 Lambda,那也就不會生成所謂的 Lambda 字節碼,也就不存在 writeReplace 方法,按照前文所分析的源碼,就無法獲取到屬性名稱。4kt28資訊網——每日最新資訊28at.com

還有小伙伴說,既然是 Lambda,那么我不用方法引用行不行?我像下面這樣寫行不行?4kt28資訊網——每日最新資訊28at.com

LambdaQueryWrapper<Book> qw = new LambdaQueryWrapper<>();qw.eq(b -> b.getId(), 2);List<Book> list = bookMapper.selectList(qw);System.out.println("list = " + list);

這也是一個 Lambda,但是如果你這樣寫了,運行之后就會報錯。為什么呢?我們來看下這個 Lambda 生成的字節碼反編譯之后是什么樣的:4kt28資訊網——每日最新資訊28at.com

final class MpDemo02ApplicationTests$$Lambda$1164 implements SFunction {    private MpDemo02ApplicationTests$$Lambda$1164() {    }    public Object apply(Object var1) {        return MpDemo02ApplicationTests.lambda$test18$3fed5817$1((Book)var1);    }    private final Object writeReplace() {        return new SerializedLambda(MpDemo02ApplicationTests.class, "com/baomidou/mybatisplus/core/toolkit/support/SFunction", "apply", "(Ljava/lang/Object;)Ljava/lang/Object;", 6, "org/javaboy/mp_demo02/MpDemo02ApplicationTests", "lambda$test18$3fed5817$1", "(Lorg/javaboy/mp_demo02/model/Book;)Ljava/lang/Object;", "(Lorg/javaboy/mp_demo02/model/Book;)Ljava/lang/Object;", new Object[0]);    }}

首先大家注意到 apply 方法生成的就不一樣,apply 里邊調用了 MpDemo02ApplicationTests.lambda$test18$3fed5817$1 方法,傳入了 Book 對象作為參數。這個方法內容相當于就是 return book.getId();。然后在 writeReplace 方法中,返回 SerializedLambda 對象的時候,implMethodName 的值就是 lambda$test18$3fed5817$1 了。回到本文一開始的源碼分析中,你會發現這樣的方法名就無法提取出來我們想要的屬性名。所以這種寫法也不對。4kt28資訊網——每日最新資訊28at.com

從這里大家也可以看到,類似于 b -> b.getId() 這樣的 Lambda,和方法引用 Book::getId 在底層是不同的。4kt28資訊網——每日最新資訊28at.com

再給小伙伴們舉個例子,比如下面一段代碼:4kt28資訊網——每日最新資訊28at.com

public class Demo01 {    public static void main(String[] args) {        Consumer<String> out1 = System.out::println;        out1.accept("javaboy");        Consumer<String> out2 = s -> System.out.println(s);        out2.accept("江南一點雨");    }}

這里有兩個輸出,第一個是一個方法引用,第二個則是一個常規的 Lambda 表達式。這兩個執行起來效果是一致的,但是底層原理不同。4kt28資訊網——每日最新資訊28at.com

先來看第一個底層生成的 Lambda 字節碼:4kt28資訊網——每日最新資訊28at.com

final class Demo01$$Lambda$14 implements Consumer {    private final PrintStream arg$1;    private Demo01$$Lambda$14(PrintStream var1) {        this.arg$1 = var1;    }    public void accept(Object var1) {        this.arg$1.println((String)var1);    }}

可以看到,這里把 System.out 的值 PrintStream 作為構造函數的參數傳進來賦值給 arg變量,當調用方法的時候,再調用1.println 方法將字符串輸出。4kt28資訊網——每日最新資訊28at.com

對于第二個底層生成的 Lambda 字節碼如下:4kt28資訊網——每日最新資訊28at.com

final class Demo01$$Lambda$16 implements Consumer {    private Demo01$$Lambda$16() {    }    public void accept(Object var1) {        Demo01.lambda$main$0((String)var1);    }}

可以看到,這里有一個新的 lambda$main$0 方法,這個方法的底層邏輯其實就是我們自定義 Lambda 的時候寫的 System.out.println(s)。4kt28資訊網——每日最新資訊28at.com

3. 小結

好啦,一篇小文,和小伙伴們探討下 MP 中 qw.eq(Book::getId, 2); 方法的底層邏輯。4kt28資訊網——每日最新資訊28at.com

本文鏈接:http://www.www897cc.com/showinfo-26-82751-0.html通過方法引用獲取屬性名的底層邏輯是什么?

聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: 深入理解C/C++指針的算術運算

下一篇: 一文讀懂Cache一致性原理

標簽:
  • 熱門焦點
  • 一加Ace2 Pro官宣:普及16G內存 引領24G

    一加官方今天繼續為本月發布的新機一加Ace2 Pro帶來預熱,公布了內存方面的信息。“淘汰 8GB ,12GB 起步,16GB 普及,24GB 引領,還有呢?#一加Ace2Pro#,2023 年 8 月,敬請期待。”同時
  • K60至尊版狂暴引擎2.0加持:超177萬跑分斬獲性能第一

    Redmi的后性能時代戰略發布會今天下午如期舉辦,在本次發布會上,Redmi公布了多項關于和聯發科的深度合作,以及新機K60 Ultra在軟件和硬件方面的特性,例如:“K60 至尊版,雙芯旗艦
  • 6月iOS設備性能榜:M2穩居榜首 A系列只能等一手3nm來救

    沒有新品發布,自然iOS設備性能榜的上榜設備就沒有什么更替,僅僅只有跑分變化而產生的排名變動,畢竟蘋果新品的發布節奏就是這樣的,一年下來也就幾個移動端新品,不會像安卓廠商,一
  • 每天一道面試題-CPU偽共享

    前言:了不起:又到了每天一到面試題的時候了!學弟,最近學習的怎么樣啊 了不起學弟:最近學習的還不錯,每天都在學習,每天都在進步! 了不起:那你最近學習的什么呢? 了不起學弟:最近在學習C
  • 小紅書1周漲粉49W+,我總結了小白可以用的N條漲粉筆記

    作者:黃河懂運營一條性教育視頻,被54萬人&ldquo;珍藏&rdquo;是什么體驗?最近,情感博主@公主是用鮮花做的,火了!僅僅憑借一條視頻,光小紅書就有超過128萬人,為她瘋狂點贊!更瘋狂的是,這
  • 騰訊VS網易,最卷游戲暑期檔,誰能笑到最后?

    作者:無銹缽來源:財經無忌7月16日晚,上海1862時尚藝術中心。伴隨著幻象的精準命中,碩大的熒幕之上,比分被定格在了14:12,被寄予厚望的EDG戰隊以絕對的優勢戰勝了BLG戰隊,拿下了總決
  • ESG的面子與里子

    來源 | 光子星球撰文 | 吳坤諺編輯 | 吳先之三伏大幕拉起,各地高溫預警不絕,但處于厄爾尼諾大&ldquo;烤&rdquo;之下的除了眾生,還有各大企業發布的ESG報告。ESG是&ldquo;環境保
  • 2299元起!iQOO Pad明晚首銷:性能最強天璣平板

    5月23日,iQOO如期舉行了新品發布會,除了首發安卓最強旗艦處理器的iQOO Neo8系列新機外,還在發布會上推出了旗下首款平板電腦——iQOO Pad,其最大的賣點
  • 與兆芯合作 聯想推出全新旗艦版筆記本電腦開天N7系列

    聯想與兆芯合作推出全新聯想旗艦版筆記本電腦開天 N7系列。這個系列采用兆芯KX-6640MA處理器平臺,KX-6640MA 處理器是采用了陸家嘴架構,16nm 工藝,4 核 4 線
Top 主站蜘蛛池模板: 苏尼特右旗| 福清市| 忻州市| 新闻| 定兴县| 陈巴尔虎旗| 沂源县| 新化县| 寻乌县| 三都| 苍南县| 达日县| 广州市| 峨山| 交城县| 广水市| 贡山| 阜康市| 哈尔滨市| 菏泽市| 甘谷县| 赤城县| 罗城| 鄯善县| 吉木乃县| 无为县| 阳信县| 监利县| 武陟县| 长兴县| 青铜峡市| 宾川县| 威远县| 龙口市| 镇坪县| 南岸区| 新营市| 伊川县| 贺兰县| 额济纳旗| 元谋县|