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

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

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

問題背景

有同學(xué)反饋,有一個(gè)項(xiàng)目從 kotlin 1.2 升級(jí)到 kotlin 1.3 以后 Spring 項(xiàng)目無法啟動(dòng),報(bào) java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'xxx' method 錯(cuò)誤

沒有引入任何其它變量,只更改了 kotlin 的版本,猜測(cè)可能是編譯出來的字節(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 值的方法是生成一個(gè)靜態(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 對(duì)于默認(rèn)參數(shù)的處理辦法就是用一個(gè) mask,告訴后面的邏輯,特定位置的參數(shù)是否需要使用默認(rèn)值。

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

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

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

咦,這樣 Spring 在掃描的時(shí)候,不會(huì)出問題嗎??jī)蓚€(gè)方法都標(biāo)注了 @GetMapping("/page") 要處理,理論上不論是 Koltin1.2 還是 1.3 在處理的時(shí)候都會(huì)出問題才對(duì)。

遇事不決,上字節(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ì)對(duì)比,發(fā)現(xiàn)只有在方法的 flags 上有一些區(qū)別,1.3 的字節(jié)碼少了 ACC_BRIDGE。

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

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

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

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

在 kotlin1.2 中,因?yàn)榫幾g出的 getActivityGameByPage$default() 包含了 bridge,在 Spring 掃描的過程中就會(huì)被忽略掉,而 kotlin1.3 中,因?yàn)榉椒ê灻话?bridge,所以被當(dāng)做了用戶自己書寫的方法,參與到掃描中,這樣 controller 就沖突了,所以報(bào)了 Ambiguous mapping 錯(cuò)誤。

如何解決

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

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

Kotlin 編譯器源碼探秘

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

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

Kotlin 新版邏輯

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

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

小結(jié)

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

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

相關(guān)文章

  • spring?retry實(shí)現(xiàn)方法請(qǐng)求重試的使用步驟

    spring?retry實(shí)現(xiàn)方法請(qǐng)求重試的使用步驟

    這篇文章主要介紹了spring?retry實(shí)現(xiàn)方法請(qǐng)求重試及使用步驟,本文分步驟通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-07-07
  • SpringBoot @ComponentScan掃描的局限性方式

    SpringBoot @ComponentScan掃描的局限性方式

    文章總結(jié):SpringBoot的@ComponentScan注解在掃描組件時(shí)存在局限性,只能掃描指定的包及其子包,無法掃描@SpringBootApplication注解自動(dòng)配置的組件,使用@SpringBootApplication注解可以解決這一問題,它集成了@Configuration、@EnableAutoConfiguration
    2025-01-01
  • Java GUI進(jìn)階之流式布局管理器FlowLayout專項(xiàng)精講

    Java GUI進(jìn)階之流式布局管理器FlowLayout專項(xiàng)精講

    FlowLayout-流式布局管理器,按水平方向依次排列放置組件,排滿一行,換下一行繼續(xù)排列。排列方向(左到右 或 右到左)取決于容器的componentOrientation屬性
    2022-04-04
  • Java中CyclicBarrier的理解與應(yīng)用詳解

    Java中CyclicBarrier的理解與應(yīng)用詳解

    這篇文章主要介紹了Java中CyclicBarrier的理解與應(yīng)用詳解,CyclicBarrier類是JUC框架中的工具類,也是一個(gè)同步輔助裝置:允許多個(gè)線程去等待直到全部線程抵達(dá)了公共的柵欄點(diǎn),需要的朋友可以參考下
    2023-12-12
  • SpringBoot結(jié)合JSR303對(duì)前端數(shù)據(jù)進(jìn)行校驗(yàn)的示例代碼

    SpringBoot結(jié)合JSR303對(duì)前端數(shù)據(jù)進(jìn)行校驗(yàn)的示例代碼

    這篇文章主要介紹了SpringBoot結(jié)合JSR303對(duì)前端數(shù)據(jù)進(jìn)行校驗(yàn)的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • springboot在idea下debug調(diào)試熱部署問題

    springboot在idea下debug調(diào)試熱部署問題

    這篇文章主要介紹了springboot在idea下debug調(diào)試熱部署問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-02-02
  • 深入剖析Java ReentrantLock的源碼

    深入剖析Java ReentrantLock的源碼

    ReentrantLock和Synchronized都是Java開發(fā)中最常用的鎖,與Synchronized這種JVM內(nèi)置鎖不同的是,ReentrantLock提供了更豐富的語(yǔ)義。本文就來深入剖析一下ReentrantLock源碼,需要的可以參考一下
    2022-11-11
  • java必學(xué)必會(huì)之方法的重載(overload)

    java必學(xué)必會(huì)之方法的重載(overload)

    java必學(xué)必會(huì)之方法的重載,介紹了方法的重載、構(gòu)造方法的重載,想要學(xué)好java方法的重載的朋友一定要好好閱讀這篇文章
    2015-12-12
  • JavaWeb工程web.xml基本配置過程解析

    JavaWeb工程web.xml基本配置過程解析

    這篇文章主要介紹了JavaWeb工程web.xml基本配置過程解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-02-02
  • 解決Java導(dǎo)入excel大量數(shù)據(jù)出現(xiàn)內(nèi)存溢出的問題

    解決Java導(dǎo)入excel大量數(shù)據(jù)出現(xiàn)內(nèi)存溢出的問題

    今天小編就為大家分享一篇解決Java導(dǎo)入excel大量數(shù)據(jù)出現(xiàn)內(nèi)存溢出的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2018-06-06

最新評(píng)論

太白县| 麻阳| 米脂县| 新河县| 布尔津县| 饶河县| 侯马市| 沽源县| 麻栗坡县| 宁安市| 虎林市| 崇左市| 宿州市| 钟山县| 黑山县| 永年县| 上饶县| 维西| 尚义县| 乃东县| 饶平县| 鄂温| 玉屏| 龙游县| 简阳市| 崇义县| 永福县| 永仁县| 庆城县| 多伦县| 双江| 禄劝| 洛川县| 白城市| 浮山县| 灌云县| 太保市| 新河县| 元阳县| 寿阳县| 高邮市|