欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

一次Spring無法啟動的問題排查實戰(zhàn)之字節(jié)碼篇

 更新時間:2022年04月07日 14:58:12   作者:挖坑的張師傅  
最近學(xué)習(xí)了spring相關(guān)知識,公司項目也用到了spring,下面這篇文章主要給大家介紹了一次Spring無法啟動的問題排查實戰(zhàn)之字節(jié)碼篇的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下

問題背景

有同學(xué)反饋,有一個項目從 kotlin 1.2 升級到 kotlin 1.3 以后 Spring 項目無法啟動,報 java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'xxx' method 錯誤

沒有引入任何其它變量,只更改了 kotlin 的版本,猜測可能是編譯出來的字節(jié)碼不一樣,出問題的函數(shù)如下。

@OptionalAuthAPI
@GetMapping("/page")
fun?getActivityGameModulePage(
????????@OptionalAuthRes?authRes:?OptionalAuthResDTO,
????????@RequestParam(name?=?"type",?defaultValue?=?"0")?type:?Int?=?0,
????????@RequestParam(name?=?"page",?defaultValue?=?"0")?page:?Int?=?0,
????????@RequestParam(name?=?"pageSize",?defaultValue?=?"30")?pageSize:?Int?=?0
):?APIResult<Page<ActivityGameModuleRespDTO>>?{
????return;
}

kotlin 處理函數(shù)中 default 值的方法是生成一個靜態(tài)的函數(shù),比如下面的函數(shù)。

class?MyTest1?{
????private?var?m?=?101

????fun?foo(x:?Int?=?100,?y:?String?=?"foo",?z:?Double?=?1.0)?{
????????println(""?+?x?+?y?+?m?+?z)
????}

????fun?bar()?{
????????foo(101,?"bar")
????????foo(101);
????????foo();
????}
}

生成的部分字節(jié)碼如下,主要看函數(shù)簽名

??public?final?void?foo(int,?java.lang.String,?double);
????descriptor:?(ILjava/lang/String;D)V
????flags:?ACC_PUBLIC,?ACC_FINAL

??public?static?void?foo$default(MyTest1,?int,?java.lang.String,?double,?int,?java.lang.Object);
????descriptor:?(LMyTest1;ILjava/lang/String;DILjava/lang/Object;)V
????flags:?ACC_PUBLIC,?ACC_STATIC,?ACC_SYNTHETIC

通過閱讀字節(jié)碼,人肉翻譯為 java 就是:

public?class?MyTest3?{
????private?int?m;
????public?void?foo(int?x,?String?y,?double?z)?{
????????String?str?=?""?+?x?+?y?+?this.m?+?z;
????????System.out.println(str);
????}

????public?void?bar()?{
????????foo$default(this,?101,?"bar",?0.0D,?4,?null);?//?4?=?b0100
????????foo$default(this,?101,?null,?0.0D,?6,?null);?//?6?=?b0110
????????foo$default(this,?0,?null,?0.0D,?7,?null);?//?7?=?b0111
????}

????public?static?void?foo$default(MyTest3?thisObj,?int?x,?String?y,?double?z,?int?mask,?Object?obj)?{
????????if?((mask?&?0x01)?!=?0)?{
????????????x?=?100;
????????}
????????if?((mask?&?0x02)?!=?0)?{
????????????y?=?"foo";
????????}
????????if?((mask?&?0x04)?!=?0)?{
????????????z?=?1.0;
????????}
????????thisObj.foo(x,?y,?z);
????}
}

由此可以看到 kotlin 對于默認(rèn)參數(shù)的處理辦法就是用一個 mask,告訴后面的邏輯,特定位置的參數(shù)是否需要使用默認(rèn)值。

回到原 getActivityGameModulePage 方法,這個方法上有兩個注解,kotlin 在編譯以后會新增一個 static 的方法

//?默認(rèn)方法
@OptionalAuthAPI
@GetMapping("/page")
public?static?
APIResult<Page<ActivityGameModuleRespDTO>>?
getActivityGameByPage(...)?{
}

//?新增方法
@OptionalAuthAPI
@GetMapping("/page")
public?static?
APIResult<Page<ActivityGameModuleRespDTO>>?
getActivityGameByPage$default(...)?{
}

咦,這樣 Spring 在掃描的時候,不會出問題嗎?兩個方法都標(biāo)注了 @GetMapping("/page") 要處理,理論上不論是 Koltin1.2 還是 1.3 在處理的時候都會出問題才對。

遇事不決,上字節(jié)碼

kotlin 1.2 編譯出來的字節(jié)碼

public?static?APIResult?getActivityGameByPage$default();
???flags:?ACC_PUBLIC,?ACC_STATIC,?ACC_BRIDGE,?ACC_SYNTHETIC

kotlin 1.3 編譯出來的字節(jié)碼

public?static?APIResult?getActivityGameByPage$default();
????flags:?ACC_PUBLIC,?ACC_STATIC,?ACC_SYNTHETIC

經(jīng)過仔細(xì)對比,發(fā)現(xiàn)只有在方法的 flags 上有一些區(qū)別,1.3 的字節(jié)碼少了 ACC_BRIDGE。

眾所周不知,ACC_BRIDGE 是一種為了實現(xiàn)某些語言特性而由編譯器自動生成的方法。除了 Kotlin,Java 自己本身在實現(xiàn)類型擦除等場景下也會用到 ACC_BRIDGE,具體我這里就不展開了,大家可以去試一下。

是不是就是這個導(dǎo)致的問題呢?

我們來看我們當(dāng)前用的 Spring 版本是如何處理方法掃描的,通過調(diào)試我們進(jìn)入到了這個方法

可以看到 Spring 4.3.10 版本判斷是否是用戶自己寫的方法時的邏輯是方法不是 bridge 且方法不處于 Object 類中,因此現(xiàn)在情況就很明朗了。

在 kotlin1.2 中,因為編譯出的 getActivityGameByPage$default() 包含了 bridge,在 Spring 掃描的過程中就會被忽略掉,而 kotlin1.3 中,因為方法簽名不包含 bridge,所以被當(dāng)做了用戶自己書寫的方法,參與到掃描中,這樣 controller 就沖突了,所以報了 Ambiguous mapping 錯誤。

如何解決

那這么嚴(yán)重的問題,難道 kotlin 不解決嗎?是的,kotlin 不解決,那就只能上層框架兼容了,Spring 在后續(xù)的版本中做了修復(fù),增加了對 ACC_SYNTHETIC 的判斷,修改的地方如下:

這樣,在新版本的 Spring 中,就不存在這個問題了,升級以后果然發(fā)現(xiàn)解決了問題。

Kotlin 編譯器源碼探秘

有了實驗的結(jié)果,反過來尋找原因就很簡單了,找到 kotlin 1.2 的源碼,然后翻一翻源碼,馬上找到了對應(yīng)的邏輯。在 4 年前的一個 commit 中,有一個伙計干掉了 ACC_BRIDGE 標(biāo)記。

對應(yīng)的源碼修改如下

Kotlin 新版邏輯

有小伙伴又試了 kotlin 1.4+,發(fā)現(xiàn)問題也消失了,這又引起了我的興趣,看了一下字節(jié)碼,發(fā)現(xiàn)新版本的 getActivityGameByPage$default() 中,已經(jīng)沒有了注解,這下從源頭解決了問題。

這下真相大白了,準(zhǔn)備落班。

小結(jié)

學(xué)一點字節(jié)碼對于我們解決 JVM、中間件的一些問題是很有幫助的,這也是我探究字節(jié)碼的動力來源,這個,又解決了一個問題吧。

到此這篇關(guān)于一次Spring無法啟動的問題排查實戰(zhàn)之字節(jié)碼篇的文章就介紹到這了,更多相關(guān)Spring無法啟動問題排查內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • jfinal添加jcaptcha驗證碼實現(xiàn)方法

    jfinal添加jcaptcha驗證碼實現(xiàn)方法

    這篇文章主要介紹了jfinal的jcaptcha驗證碼實現(xiàn)方法,大家參考使用吧
    2014-01-01
  • 基于jmeter實現(xiàn)跨線程組傳遞token過程圖解

    基于jmeter實現(xiàn)跨線程組傳遞token過程圖解

    這篇文章主要介紹了基于jmeter實現(xiàn)跨線程組傳遞token,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-04-04
  • java如何寫接口給別人調(diào)用的示例代碼

    java如何寫接口給別人調(diào)用的示例代碼

    這篇文章主要介紹了java如何寫接口給別人調(diào)用的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-09-09
  • Java詳細(xì)講解依賴注入的方式

    Java詳細(xì)講解依賴注入的方式

    Idea中使用@Autowire注解會出現(xiàn)提示黃線,強(qiáng)迫癥患者看著很難受,使用構(gòu)造器注入或者setter方法注入后可解決,下面我們一起來看看
    2022-06-06
  • 將Java程序與數(shù)據(jù)庫進(jìn)行連接的操作方法

    將Java程序與數(shù)據(jù)庫進(jìn)行連接的操作方法

    這篇文章主要介紹了將Java程序與數(shù)據(jù)庫進(jìn)行連接的操作方法,是Java入門學(xué)習(xí)中的基礎(chǔ)知識,需要的朋友可以參考下
    2015-10-10
  • 解決Idea運行junit測試時報Error:[3,17]?程序包org.junit不存在的問題

    解決Idea運行junit測試時報Error:[3,17]?程序包org.junit不存在的問題

    這篇文章主要介紹了Idea運行junit測試時報Error:[3,17]?程序包org.junit不存在解決方法,本文給大家分享兩種解決辦法,需要的朋友可以參考下
    2023-03-03
  • Java不借助第三變量實現(xiàn)兩數(shù)交換的示例

    Java不借助第三變量實現(xiàn)兩數(shù)交換的示例

    本文主要介紹了Java不借助第三變量實現(xiàn)兩數(shù)交換的示例,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • java中的類型擦除type?erasure示例詳解

    java中的類型擦除type?erasure示例詳解

    泛型是java從JDK?5開始引入的新特性,泛型的引入可以讓我們在代碼編譯的時候就強(qiáng)制檢查傳入的類型,從而提升了程序的健壯度,泛型可以用在類和接口上,在集合類中非常常見,本文將會講解泛型導(dǎo)致的類型擦除
    2023-09-09
  • JDBC連接Mysql的5種方式實例總結(jié)

    JDBC連接Mysql的5種方式實例總結(jié)

    JDBC是Java DataBase Connectivity技術(shù)的簡稱,是一種可用于執(zhí)行 SQL語句的Java API,下面這篇文章主要給大家介紹了關(guān)于JDBC連接Mysql的5種方式,文中通過實例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-04-04
  • java map遍歷的四種方法總結(jié)

    java map遍歷的四種方法總結(jié)

    以下是我整理的關(guān)于java中map的遍歷的四種方法。需要的朋友可以過來參考下,希望對大家有所幫助
    2013-10-10

最新評論