亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

spring中前端明明傳了值后端卻接收不到問題解決辦法

 更新時間:2024年05月15日 09:44:20   作者:AI?CODE  
在學(xué)習(xí)Spring的時候遇到了一個問題,后臺一直接收不到前臺傳遞過來的參數(shù),耽誤了好長時間終于找到了原因,這篇文章主要給大家介紹了關(guān)于spring中前端明明傳了值后端卻接收不到問題的解決辦法,需要的朋友可以參考下

問題場景

在進(jìn)行前后端的聯(lián)調(diào)時,有時候會出現(xiàn),前端明明傳了值,后端接口卻接收不到的情況,這種情況常常讓人很苦惱,然后就會去仔細(xì)對比前后端的參數(shù)單詞是不是對應(yīng)上了,也會去檢查是不是前端的請求參數(shù)格式有問題,又或者是后端接口接收的參數(shù)格式有問題,一通檢查對比下來,發(fā)現(xiàn)都沒問題。那究竟是為什么呢?那就繼續(xù)往下看吧。

問題重現(xiàn)

控制層代碼:

    @PostMapping(value = "/test")
    public void test(@RequestBody UserVO userVO) {

        System.out.println("用戶代碼:" + userVO.getUCode());
        System.out.println("用戶名稱:" + userVO.getUName());
    }

參數(shù)實(shí)體類:UserVO

@Data
public class UserVO {
    /**
     * 用戶代碼
     */
    private Long uCode;

    /**
     * 用戶名稱
     */
    private String uName;
}

用postman模擬前端調(diào)用:

控制臺預(yù)期打印結(jié)果:

用戶代碼:12345
用戶名稱:小明

控制臺實(shí)際打印結(jié)果:

解決方式

在實(shí)體類的屬性上方加@JsonProperty注解,如下圖:

然后測試控制臺打印結(jié)果:

原因分析

首先我們先把實(shí)體類復(fù)原,并且加上一個新的屬性loginType

@Data
public class UserVO {

    /**
     * 用戶代碼
     */
    private Long uCode;

    /**
     * 用戶名稱
     */
    private String uName;

    /**
     * 登錄類型
     */
    private String loginType;

}

眼尖的同學(xué)可能會發(fā)現(xiàn)了,我新加的屬性loginType長得是不是跟原來兩個屬性uCode和uName不太一樣,不一樣的點(diǎn)在于uCode和uName都是首字母小寫,第二個字母大寫的單詞,而loginType則不然。但是它們?nèi)挤像劮迕ǖ囊?guī)范,對吧。這時候可以猜測,難道是這個原因?qū)е碌模?/p>

在這里我們先來簡單驗(yàn)證下uCode、uName、loginType的情況

通過斷點(diǎn)發(fā)現(xiàn),uCode、uName是空的,loginType卻不是空的

然后我們將uCode、uName分別改為userCode、userName后再進(jìn)行測試

@Data
public class UserVO {
    /**
     * 用戶代碼
     */
    private Long userCode;
    /**
     * 用戶名稱
     */
    private String userName;
    /**
     * 登錄類型
     */
    private String loginType;
}

這個時候我們就可以得出結(jié)論,原因就是首字母小寫,第二個字母大寫的單詞的屬性是有問題的。

但是我們不禁要問,為啥呢?它這也符合駝峰命名法的規(guī)范啊。為什么它就有問題呢?感興趣的同學(xué)可以接著往下看。

原理分析

首先我們要知道,在Spring中,前后端之間數(shù)據(jù)傳輸會涉及到數(shù)據(jù)的序列化和反序列化的操作,并且SpringBoot默認(rèn)是使用Jackson作為JSON數(shù)據(jù)格式處理的類庫。

序列化:按照指定的格式、順序等將實(shí)體類對象轉(zhuǎn)換為JSON對象;
反序列化:將JSON對象中的字符串、數(shù)字等,將其轉(zhuǎn)換為實(shí)體對象;

那么現(xiàn)在咱們就來斷點(diǎn)調(diào)試Jackson的源碼來看看原因。為方便展示,我將實(shí)體類留下uName、loginType兩個屬性

@Data
public class UserVO {
    /**
     * 用戶名稱
     */
    private String uName;
    /**
     * 登錄類型
     */
    private String loginType;
}

開始調(diào)試:

Jackon主要是通過抽象類AbstractJackson2HttpMessageConverterreadJavaType方法將 HTTP 請求中的消息體轉(zhuǎn)換為對象,所以我們找到這部分代碼,對他進(jìn)行斷點(diǎn)調(diào)試:

然后逐步斷點(diǎn),在上圖的第192行和第195行,它會調(diào)用ObjectMapper.readValue,然后斷點(diǎn)推進(jìn)到調(diào)用方法的核心地方ObjectMapper_readMapAndClose方法

this._findRootDeserializer(ctxt, valueType);的大概意思就是根據(jù)類型找到反序列化器,注意在這邊是先從緩存中取,取到了的話就直接返回了。如果沒到下一步斷點(diǎn),在這邊你可以清除一下緩存。

然后斷點(diǎn)繼續(xù)推進(jìn)到創(chuàng)建反序列化器的地方DeserializerCache._createDeserializer

如果你清除緩存或者重啟項(xiàng)目在調(diào)用時會直接進(jìn)入到這個創(chuàng)建反序列化器的地方,你直接在這個方法上打斷點(diǎn)就好了

找到上圖中第164行的代碼,BeanDescription是類的描述的意思,所有的屬性都在這里被解析,然后我們斷點(diǎn)進(jìn)去看看。會進(jìn)入到POJOPropertiesCollector.collectAll方法,就是字面意思,收集所有。方法邏輯詳見下圖:

執(zhí)行完this._addFields(props);props加入了uNameloginType

執(zhí)行完this._addMethods(props);后發(fā)現(xiàn)props竟然多了一個uname

在這里我們點(diǎn)開屬性詳細(xì)去看,會發(fā)現(xiàn)uName的get和set為空,但是loginType的是正常的,并且uname這個不知道哪里跑出來的屬性的get和set也是不為空的。

再接著執(zhí)行this._removeUnwantedProperties(props);移除不想要的屬性之后,會發(fā)現(xiàn)就剩下loginTypeuname了,因?yàn)?code>uName沒有g(shù)et和set。為什么

然后props中目前存儲的就是loginTypeuname

現(xiàn)在我們就要弄明白為什么有g(shù)et/set的是uname而不是uName

首先,在這個例子中我使用的是@Data注解,也就是使用的 Lombok,也就是說 getter 和 setter 是由 Lombok 生成的。使用注解的話會將get/set方法隱藏起來,然后我們可以通過IDEA的Structure來看,見下圖:

那么Jackson 到底是如何解析的,使得解析出來的是uname,而不是uName。它解析的具體代碼在com.fasterxml.jackson.databind.util.BeanUtil類中的legacyManglePropertyName方法中

從上圖為我們可以很明顯的看到,通過這個方法之后getLoginType被解析成loginType了。那我們再來看看uName,見下圖:

從上圖斷點(diǎn)我們可以清晰的看見getUName被解析成uname了,按照我們正常的思維邏輯的話,loginType和uName都符合駝峰命名法的規(guī)范,那么uName對應(yīng)的get方法解析出來應(yīng)該是uName啊,為什么變成了uname呢?原因就在于這個

legacyManglePropertyName方法的處理邏輯,它的邏輯大概是:

1.根據(jù)入?yún)ffset去除get或者get,然后就剩下UName或者LoginType了
2.然后從第一個字母開始解析,如果第一個字母是大寫的,于是就將它轉(zhuǎn)成小寫,然后找下一個,如果還是大寫,就繼續(xù)轉(zhuǎn)成小寫,直到找到一個小寫字母后,就把之后的字母(不管大小寫)一起拼接進(jìn)來。

這樣就能解釋了:

去除get之后的LoginType找到第一個字母是大寫,轉(zhuǎn)為小寫的l,下一個字母是小寫的了,就直接把后面的全拼接進(jìn)來,最終形成了loginType

去除get之后的UName找到第一個字母是大寫,轉(zhuǎn)為小寫的u,下一個字母又是大寫,轉(zhuǎn)為小寫的n,在下一個字母是小寫的了,就直接把后面的全拼接進(jìn)來,最終形成了uname

如果說這邊的getUName換成getuName,那么解析出來的就是正確的uName了。

結(jié)論

到這里,我們就可以得出結(jié)論了

因?yàn)?Lombok 生成 get、set 方法的語義規(guī)范與和Jackson 處理 get、set 方法之間的不一致,導(dǎo)致屬性名無法匹配上,最終也就導(dǎo)致了前端明明傳了參數(shù),后端卻接收不到的問題。

擴(kuò)展

我后面去github的 lombok社區(qū) 了解了相關(guān)內(nèi)容,lombook社區(qū)是這樣描述的:

用網(wǎng)頁翻譯給他翻譯成中文,翻譯有些不對,但是能看明白大概意思就行

lombok的大概意思就是:我就是這樣的規(guī)范,即使其他的工具框架都改了,我也不改,但是建議你們不要使用首字母小寫第二個字母大寫的屬性名,避免出現(xiàn)問題,可能知名度比較高的框架都比較傲嬌吧哈哈。

但是lombok還是給出了一個解決方案,加上這個配置項(xiàng)

lombok.accessors.capitalization = [basic | beanspec] (default: basic)

其中basic代表遵循lombok的規(guī)范(getUName);beanspec代表遵循Spring、Jackson 的規(guī)范(getuName)。默認(rèn)是basic。

看到這里,我就來總結(jié)一下能解決這個問題的三種方案吧

1. 加@JsonProperty注解強(qiáng)行指定屬性名

@Data
public class UserVO {
    /**
     * 用戶名稱
     */
    @JsonProperty(value = "uName")
    private String uName;
    /**
     * 登錄類型
     */
    private String loginType;
}

2.不使用lombok,使用IDEA默認(rèn)生成get/set方法

3.加上lombok配置項(xiàng)

lombok.accessors.capitalization = [basic | beanspec] (default: basic)

最后,博主的建議是,盡量不要用這種命名方式,如果非要用,那就加上@JsonProperty注解強(qiáng)行指定屬性名,這樣比較方便。

總結(jié)

到此這篇關(guān)于spring中前端明明傳了值后端卻接收不到問題解決辦法的文章就介紹到這了,更多相關(guān)spring前端傳值后端接收不到內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java中使用同步回調(diào)和異步回調(diào)的示例詳解

    Java中使用同步回調(diào)和異步回調(diào)的示例詳解

    這篇文章主要介紹了Java中使用同步回調(diào)和異步回調(diào)的相關(guān)資料,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-04-04
  • java計算π的多種方法

    java計算π的多種方法

    這篇文章主要介紹了使用java計算π的多種方法,代碼詳細(xì),邏輯清晰,對于算法思路可能有所幫助,需要的朋友可以參考下
    2021-04-04
  • spring boot實(shí)戰(zhàn)教程之shiro session過期時間詳解

    spring boot實(shí)戰(zhàn)教程之shiro session過期時間詳解

    這篇文章主要給大家介紹了關(guān)于spring boot實(shí)戰(zhàn)教程之shiro session過期時間的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起看看吧。
    2017-10-10
  • Java基礎(chǔ)學(xué)習(xí)之構(gòu)造方法詳解

    Java基礎(chǔ)學(xué)習(xí)之構(gòu)造方法詳解

    這篇文章主要為大家詳細(xì)介紹了Java基礎(chǔ)學(xué)習(xí)中構(gòu)造方法的概述及注意事項(xiàng),文中的示例代碼講解詳細(xì),對我們學(xué)習(xí)Java有一定幫助,需要的可以參考一下
    2022-08-08
  • SpringCloud之Ribbon使用示例解析

    SpringCloud之Ribbon使用示例解析

    這篇文章主要為大家介紹了SpringCloud之Ribbon使用示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-06-06
  • 使用mtrace追蹤JVM堆外內(nèi)存泄露的方法

    使用mtrace追蹤JVM堆外內(nèi)存泄露的方法

    這篇文章主要給大家介紹了如何使用mtrace追蹤JVM堆外內(nèi)存泄露,文章通過代碼示例介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下
    2023-09-09
  • 解決dubbo錯誤ip及ip亂入問題的方法

    解決dubbo錯誤ip及ip亂入問題的方法

    今天小編就為大家分享一篇關(guān)于解決dubbo錯誤ip及ip亂入問題的方法,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-03-03
  • Java事務(wù)管理學(xué)習(xí)之Spring和Hibernate詳解

    Java事務(wù)管理學(xué)習(xí)之Spring和Hibernate詳解

    這篇文章主要給大家介紹了Java事務(wù)管理學(xué)習(xí)之Spring和Hibernate的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友們可以參考借鑒,下面來一起看看吧。
    2017-03-03
  • @SpringBootTest 注解報紅問題及解決

    @SpringBootTest 注解報紅問題及解決

    這篇文章主要介紹了@SpringBootTest 注解報紅問題及解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • 深入講解RocketMQ原理

    深入講解RocketMQ原理

    這篇文章主要介紹了詳解SpringBoot整合RocketMQ,RocketMQ作為一款純java、分布式、隊列模型的開源消息中間件,支持事務(wù)消息、順序消息、批量消息、定時消息、消息回溯等,需要的朋友可以參考下
    2023-07-07

最新評論