AVt天堂网 手机版,亚洲va久久久噜噜噜久久4399,天天综合亚洲色在线精品,亚洲一级Av无码毛片久久精品

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

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

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

很多小伙伴可能都用過 MyBatis-Plus,這里邊我們構造 where 條件的時候,可以直接通過方法引用的方式去指定屬性名:BaF28資訊網——每日最新資訊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 就可以識別出來這里的屬性名。BaF28資訊網——每日最新資訊28at.com

1. 源碼分析

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

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

這里有一個 meta.getImplMethodName() 方法,這個方法的拿到的其實就是我們 Lambda 表達式中的方法名,也就是 getId,然后再通過 PropertyNamer.methodToProperty 對這個方法名進行處理,最終拿到屬性名:BaF28資訊網——每日最新資訊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 這些去掉,然后剩余的字符串首字母小寫之后返回。BaF28資訊網——每日最新資訊28at.com

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

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

2. writeReplace

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

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

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

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

以本文一開頭的 Lambda 表達式為例,最終生成的字節碼反編譯之后,內容如下:BaF28資訊網——每日最新資訊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 方法。BaF28資訊網——每日最新資訊28at.com

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

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

3. 擴展知識

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

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

還有小伙伴說,既然是 Lambda,那么我不用方法引用行不行?我像下面這樣寫行不行?BaF28資訊網——每日最新資訊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 生成的字節碼反編譯之后是什么樣的:BaF28資訊網——每日最新資訊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 了。回到本文一開始的源碼分析中,你會發現這樣的方法名就無法提取出來我們想要的屬性名。所以這種寫法也不對。BaF28資訊網——每日最新資訊28at.com

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

再給小伙伴們舉個例子,比如下面一段代碼:BaF28資訊網——每日最新資訊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 表達式。這兩個執行起來效果是一致的,但是底層原理不同。BaF28資訊網——每日最新資訊28at.com

先來看第一個底層生成的 Lambda 字節碼:BaF28資訊網——每日最新資訊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 方法將字符串輸出。BaF28資訊網——每日最新資訊28at.com

對于第二個底層生成的 Lambda 字節碼如下:BaF28資訊網——每日最新資訊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)。BaF28資訊網——每日最新資訊28at.com

3. 小結

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

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

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

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

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

標簽:
  • 熱門焦點
  • 2023年Q2用戶偏好榜:12+256G版本成新主流

    3月份的性能榜、性價比榜和好評榜之后,就要輪到2023年的第二季度偏好榜了,上半年的新機潮已經過去,最明顯的肯定就是大內存和存儲的機型了,另外部分中端機也取消了屏幕塑料支架
  • K6:面向開發人員的現代負載測試工具

    K6 是一個開源負載測試工具,可以輕松編寫、運行和分析性能測試。它建立在 Go 和 JavaScript 之上,它被設計為功能強大、可擴展且易于使用。k6 可用于測試各種應用程序,包括 Web
  • 十個可以手動編寫的 JavaScript 數組 API

    JavaScript 中有很多API,使用得當,會很方便,省力不少。 你知道它的原理嗎? 今天這篇文章,我們將對它們進行一次小總結?,F在開始吧。1.forEach()forEach()用于遍歷數組接收一參
  • 企業采用CRM系統的11個好處

    客戶關系管理(CRM)軟件可以為企業提供很多的好處,從客戶保留到提高生產力?! RM軟件用于企業收集客戶互動,以改善客戶體驗和滿意度?! RM軟件市場規模如今超過580
  • 如何使用JavaScript創建一只圖像放大鏡?

    譯者 | 布加迪審校 | 重樓如果您曾經瀏覽過購物網站,可能遇到過圖像放大功能。它可以讓您放大圖像的特定區域,以便瀏覽。結合這個小小的重要功能可以大大改善您網站的用戶體驗
  • 零售大模型“干中學”,攀爬數字化珠峰

    文/侯煜編輯/cc來源/華爾街科技眼對于絕大多數登山愛好者而言,攀爬珠穆朗瑪峰可謂終極目標。攀登珠峰的商業路線有兩條,一是尼泊爾境內的南坡路線,一是中國境內的北坡路線。相
  • 華為Mate60標準版細節曝光:經典星環相機模組回歸

    這段時間以來,關于華為新旗艦的爆料日漸密集。據此前多方爆料,今年華為將開始恢復一年雙旗艦戰略,除上半年推出的P60系列外,往年下半年的Mate系列也將
  • 華為Mate60系列模具曝光:采用碩大圓形后置相機模組+拼接配色方案

    據此前多方爆料,今年華為將開始恢復一年雙旗艦戰略,除上半年推出的P60系列外,往年下半年的Mate系列也將迎來更新,有望在9-10月份帶來全新的華為Mate60
  • 質感不錯!OPPO K11渲染圖曝光:旗艦IMX890傳感器首次下放

    一直以來,OPPO K系列機型都保持著較為均衡的產品體驗,歷來都是2K價位的明星機型,去年推出的OPPO K10和OPPO K10 Pro兩款機型憑借各自的出色配置,堪稱有
Top