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

@NonNull導(dǎo)致無法序列化的問題及解決

 更新時(shí)間:2023年01月06日 17:02:38   作者:if_icanfly  
這篇文章主要介紹了@NonNull導(dǎo)致無法序列化的問題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

@NonNull導(dǎo)致無法序列化的問題

以上這個(gè)代碼在接參的時(shí)候報(bào)了一個(gè)缺少無參構(gòu)造函數(shù)無法序列化的錯(cuò)誤

將.class反編譯

可以看到編譯后的源碼中生成了一個(gè)有參構(gòu)造 明顯是 用來判空的 假設(shè)那么這個(gè)構(gòu)造函數(shù)應(yīng)該就是根據(jù)@NonNull生成的

實(shí)際上我們治理應(yīng)該添加的注解是NotNull才對(duì)

上面因?yàn)閘ombook根據(jù)@NonNull生成了一個(gè)有參構(gòu)造函數(shù),導(dǎo)致jdk不會(huì)添加默認(rèn)的無參構(gòu)造函數(shù),沒有無參構(gòu)造函數(shù)的話 序列化就會(huì)失敗.

@NonNull修飾Field反序列化部分值為空

一般Http接口,為了參數(shù)統(tǒng)一管理,定義一個(gè)VO用來接收POST過來的字段,常規(guī)做法是把參數(shù)解析成map,然后反序列化到VO中,早期定義的接口字段都非空,所以VO中都加了@NonNull注解;一直很和諧;

因?yàn)樾枨笞兓?,接口字段需要增加兩個(gè),為了版本兼容,新加的兩個(gè)字段需要可空;于是在VO中增加兩個(gè)字段,不用@NonNull修飾,但是反序列化后發(fā)現(xiàn)這兩個(gè)字段一直為空!怎么都不能從map中獲取到這兩個(gè)值!

分析

版本:

  • JDK:1.8
  • lombok:1.18.12
  • fastjson:1.2.60

原代碼

package com.example.demo;

import lombok.Data;
import lombok.NonNull;

@Data
public class DemoRequestVO {

    @NonNull
    private String firstParam;

    private String SecondParam;

    private String thirdParam;

}
 public static void testDemo(){

        Map<String, String> params = new HashMap<>();
        params.put("firstParam","lllllll");
        params.put("secondParam","45454645");
        params.put("thirdParam","xx公司");
        
        DemoRequestVO request = JSON.parseObject(JSON.toJSONString(params), DemoRequestVO.class);
        System.out.println(request);
    }

分析原因

做兩方面猜測:

1: 注解提供者問題

  • 2: Json反序列化問題
  • 1: 先看下: 注解提供者 @NonNull

發(fā)現(xiàn)其是作用于RetentionPolicy.CLASS的

package lombok;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE, ElementType.TYPE_USE})
@Retention(RetentionPolicy.CLASS)
@Documented
public @interface NonNull {
}

查看lombok源碼可以看到,NonNull注解提供者一共這么多

static {
?? ??? ?NONNULL_ANNOTATIONS = Collections.unmodifiableList(Arrays.asList(new String[] {
?? ??? ??? ?"androidx.annotation.NonNull",
?? ??? ??? ?"android.support.annotation.NonNull",
?? ??? ??? ?"com.sun.istack.internal.NotNull",
?? ??? ??? ?"edu.umd.cs.findbugs.annotations.NonNull",
?? ??? ??? ?"javax.annotation.Nonnull",
?? ??? ??? ?// "javax.validation.constraints.NotNull", // The field might contain a null value until it is persisted.
?? ??? ??? ?"lombok.NonNull",
?? ??? ??? ?"org.checkerframework.checker.nullness.qual.NonNull",
?? ??? ??? ?"org.eclipse.jdt.annotation.NonNull",
?? ??? ??? ?"org.eclipse.jgit.annotations.NonNull",
?? ??? ??? ?"org.jetbrains.annotations.NotNull",
?? ??? ??? ?"org.jmlspecs.annotation.NonNull",
?? ??? ??? ?"org.netbeans.api.annotations.common.NonNull",
?? ??? ??? ?"org.springframework.lang.NonNull",
?? ??? ?}));

再看下經(jīng)過注解后編譯的CLASS

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.example.demo;

import lombok.NonNull;

public class DemoRequestVO {
? ? @NonNull
? ? private String firstParam;
? ? private String SecondParam;
? ? private String thirdParam;

? ? public DemoRequestVO(@NonNull final String firstParam) {
? ? ? ? if (firstParam == null) {
? ? ? ? ? ? throw new NullPointerException("firstParam is marked non-null but is null");
? ? ? ? } else {
? ? ? ? ? ? this.firstParam = firstParam;
? ? ? ? }
? ? }

? ? @NonNull
? ? public String getFirstParam() {
? ? ? ? return this.firstParam;
? ? }

? ? public String getSecondParam() {
? ? ? ? return this.SecondParam;
? ? }

? ? public String getThirdParam() {
? ? ? ? return this.thirdParam;
? ? }

? ? public void setFirstParam(@NonNull final String firstParam) {
? ? ? ? if (firstParam == null) {
? ? ? ? ? ? throw new NullPointerException("firstParam is marked non-null but is null");
? ? ? ? } else {
? ? ? ? ? ? this.firstParam = firstParam;
? ? ? ? }
? ? }

? ? public void setSecondParam(final String SecondParam) {
? ? ? ? this.SecondParam = SecondParam;
? ? }

? ? public void setThirdParam(final String thirdParam) {
? ? ? ? this.thirdParam = thirdParam;
? ? }

? ? public boolean equals(final Object o) {
? ? ? ? if (o == this) {
? ? ? ? ? ? return true;
? ? ? ? } else if (!(o instanceof DemoRequestVO)) {
? ? ? ? ? ? return false;
? ? ? ? } else {
? ? ? ? ? ? DemoRequestVO other = (DemoRequestVO)o;
? ? ? ? ? ? if (!other.canEqual(this)) {
? ? ? ? ? ? ? ? return false;
? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? label47: {
? ? ? ? ? ? ? ? ? ? Object this$firstParam = this.getFirstParam();
? ? ? ? ? ? ? ? ? ? Object other$firstParam = other.getFirstParam();
? ? ? ? ? ? ? ? ? ? if (this$firstParam == null) {
? ? ? ? ? ? ? ? ? ? ? ? if (other$firstParam == null) {
? ? ? ? ? ? ? ? ? ? ? ? ? ? break label47;
? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? } else if (this$firstParam.equals(other$firstParam)) {
? ? ? ? ? ? ? ? ? ? ? ? break label47;
? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? ? ? return false;
? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? Object this$SecondParam = this.getSecondParam();
? ? ? ? ? ? ? ? Object other$SecondParam = other.getSecondParam();
? ? ? ? ? ? ? ? if (this$SecondParam == null) {
? ? ? ? ? ? ? ? ? ? if (other$SecondParam != null) {
? ? ? ? ? ? ? ? ? ? ? ? return false;
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? } else if (!this$SecondParam.equals(other$SecondParam)) {
? ? ? ? ? ? ? ? ? ? return false;
? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? Object this$thirdParam = this.getThirdParam();
? ? ? ? ? ? ? ? Object other$thirdParam = other.getThirdParam();
? ? ? ? ? ? ? ? if (this$thirdParam == null) {
? ? ? ? ? ? ? ? ? ? if (other$thirdParam != null) {
? ? ? ? ? ? ? ? ? ? ? ? return false;
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? } else if (!this$thirdParam.equals(other$thirdParam)) {
? ? ? ? ? ? ? ? ? ? return false;
? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? return true;
? ? ? ? ? ? }
? ? ? ? }
? ? }

? ? protected boolean canEqual(final Object other) {
? ? ? ? return other instanceof DemoRequestVO;
? ? }

? ? public int hashCode() {
? ? ? ? int PRIME = true;
? ? ? ? int result = 1;
? ? ? ? Object $firstParam = this.getFirstParam();
? ? ? ? int result = result * 59 + ($firstParam == null ? 43 : $firstParam.hashCode());
? ? ? ? Object $SecondParam = this.getSecondParam();
? ? ? ? result = result * 59 + ($SecondParam == null ? 43 : $SecondParam.hashCode());
? ? ? ? Object $thirdParam = this.getThirdParam();
? ? ? ? result = result * 59 + ($thirdParam == null ? 43 : $thirdParam.hashCode());
? ? ? ? return result;
? ? }

? ? public String toString() {
? ? ? ? return "DemoRequestVO(firstParam=" + this.getFirstParam() + ", SecondParam=" + this.getSecondParam() + ", thirdParam=" + this.getThirdParam() + ")";
? ? }
}

重點(diǎn)是看這個(gè)編譯后的class的構(gòu)造方法:只有一個(gè)帶@NonNull注解參數(shù)的構(gòu)造方法?。?!

一般到這里都能想到反序列化后的為啥另外兩個(gè)未注解NonNull的為啥空值了;如果沒想到,也沒關(guān)系,咱們?cè)賮砜纯碕SON反序列化的過程

2: json反序列化;

一系列遞進(jìn)過程不再描述,重點(diǎn)看JavaBeanInfo類中的build方法,這個(gè)方法是真正把map反序化到j(luò)avaBean的過程

public static JavaBeanInfo build(Class<?> clazz, Type type, PropertyNamingStrategy propertyNamingStrategy, boolean fieldBased, boolean compatibleWithJavaBean, boolean jacksonCompatible)?

挑幾處重要的開看下:

取構(gòu)造方法list

?? ??? ?Constructor[] constructors = clazz.getDeclaredConstructors();
? ? ? ? Constructor<?> defaultConstructor = null;
? ? ? ? if (!kotlin || constructors.length == 1) {
? ? ? ? ? ? if (builderClass == null) {
? ? ? ? ? ? ? ? defaultConstructor = getDefaultConstructor(clazz, constructors);
? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? defaultConstructor = getDefaultConstructor(builderClass, builderClass.getDeclaredConstructors());
? ? ? ? ? ? }
? ? ? ? }

賦值創(chuàng)建javaBean的構(gòu)造

? ?boolean is_public = (constructor.getModifiers() & 1) != 0;
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? if (is_public) {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? String[] lookupParameterNames = ASMUtils.lookupParameterNames(constructor);
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? if (lookupParameterNames != null && lookupParameterNames.length != 0 && (creatorConstructor == null || paramNames == null || lookupParameterNames.length > paramNames.length)) {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? paramNames = lookupParameterNames;
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? creatorConstructor = constructor;
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }

創(chuàng)建javaBean,看傳參; 只有一個(gè)構(gòu)造方法;

?if (!kotlin && !clazz.getName().equals("javax.servlet.http.Cookie")) {
? ? ? ? ? ? ? ? ? ? ? ? return new JavaBeanInfo(clazz, builderClass, (Constructor)null, creatorConstructor, (Method)null, (Method)null, jsonType, fieldList);
? ? ? ? ? ? ? ? ? ? }

結(jié)論:

使用@NonNull注解,編譯后生成的CLASS構(gòu)造方法只有一個(gè),且只有被注解的字段才能構(gòu)造時(shí)候賦值;此種做法是保證在編譯期可以判斷非空;

反序列化時(shí)候使用了這個(gè)構(gòu)造方法,其他的值沒有被賦值;

建議改進(jìn)

1: 使用@NotNull代替

2: 如果修飾的是String類型,推薦使用@NotBlank,好處是可以判斷空字符串

3: 在自定義的VO中增加一個(gè)無參構(gòu)造方法;

總結(jié)

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • SpringBoot實(shí)現(xiàn)自動(dòng)配置的方式詳解

    SpringBoot實(shí)現(xiàn)自動(dòng)配置的方式詳解

    Spring Boot 自動(dòng)配置 是其核心特性之一,它通過智能化的默認(rèn)配置減少了開發(fā)者的工作量,自動(dòng)配置的原理基于條件化配置和 Spring 的 @Configuration 機(jī)制,本文給大家講解了SpringBoot實(shí)現(xiàn)自動(dòng)配置的過程,需要的朋友可以參考下
    2025-04-04
  • Spring @Configuration注解及配置方法

    Spring @Configuration注解及配置方法

    這篇文章主要介紹了Spring @Configuration注解及配置方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-04-04
  • Java8新特性之新日期時(shí)間庫的使用教程

    Java8新特性之新日期時(shí)間庫的使用教程

    這篇文章主要給大家介紹了關(guān)于Java8新特性之新日期時(shí)間庫使用的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-12-12
  • SpringBoot處理請(qǐng)求參數(shù)中包含特殊符號(hào)

    SpringBoot處理請(qǐng)求參數(shù)中包含特殊符號(hào)

    今天寫代碼遇到了一個(gè)問題,請(qǐng)求參數(shù)是個(gè)路徑“D:/ExcelFile”,本文就詳細(xì)的介紹一下該錯(cuò)誤的解決方法,感興趣的可以了解一下
    2021-06-06
  • Netty的心跳檢測解析

    Netty的心跳檢測解析

    這篇文章主要介紹了Netty的心跳檢測解析,客戶端的心跳檢測對(duì)于任何長連接的應(yīng)用來說,都是一個(gè)非?;A(chǔ)的功能,要理解心跳的重要性,首先需要從網(wǎng)絡(luò)連接假死的現(xiàn)象說起,需要的朋友可以參考下
    2023-12-12
  • SpringBoot3通過GraalVM生成exe執(zhí)行文件問題

    SpringBoot3通過GraalVM生成exe執(zhí)行文件問題

    文章介紹了如何安裝GraalVM和Visual Studio,并通過Spring Boot項(xiàng)目將Java應(yīng)用程序封裝成可執(zhí)行文件(.exe)
    2024-12-12
  • MyBatis XML方式的基本用法之多表查詢功能的示例代碼

    MyBatis XML方式的基本用法之多表查詢功能的示例代碼

    這篇文章主要介紹了MyBatis XML方式的基本用法之多表查詢功能的示例代碼,本文通過示例文字相結(jié)合的形式給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-07-07
  • Java內(nèi)存分配多種情況的用法解析

    Java內(nèi)存分配多種情況的用法解析

    這篇文章主要介紹了Java內(nèi)存分配多種情況的用法解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-11-11
  • Java學(xué)習(xí)常用包(類)之java.util包詳解

    Java學(xué)習(xí)常用包(類)之java.util包詳解

    這篇文章主要介紹了Java學(xué)習(xí)常用包(類)之java.util包的相關(guān)資料,Java.util包是Java標(biāo)準(zhǔn)類庫的重要組成部分,包含集合框架、日期時(shí)間類、事件模型、隨機(jī)數(shù)生成器等實(shí)用工具類,集合框架提供了多種數(shù)據(jù)結(jié)構(gòu)和算法,需要的朋友可以參考下
    2024-10-10
  • Java Graphics實(shí)現(xiàn)界面顯示文字并換行

    Java Graphics實(shí)現(xiàn)界面顯示文字并換行

    Java中Graphics類提供了一些基本的幾何圖形繪制方法,本文將利用Graphics實(shí)現(xiàn)界面顯示文字并換行效果,感興趣的小伙伴可以動(dòng)手嘗試一下
    2022-08-08

最新評(píng)論

望城县| 肥西县| 广昌县| 潜江市| 专栏| 鹿泉市| 五大连池市| 安远县| 禹城市| 赣州市| 明水县| 阜南县| 宝应县| 乌什县| 错那县| 贵溪市| 康保县| 四会市| 湖北省| 天峻县| 中牟县| 永和县| 博罗县| 平湖市| 大悟县| 西乡县| 徐汇区| 文昌市| 长武县| 远安县| 松潘县| 乌鲁木齐县| 平舆县| 宁南县| 温宿县| 温泉县| 吉首市| 小金县| 连州市| 大渡口区| 手游|