我的编程空间,编程开发者的网络收藏夹
学习永远不晚

@NonNull导致无法序列化的问题及解决

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

@NonNull导致无法序列化的问题及解决

@NonNull导致无法序列化的问题

以上这个代码在接参的时候报了一个缺少无参构造函数无法序列化的错误

将.class反编译

可以看到编译后的源码中生成了一个有参构造 明显是 用来判空的 假设那么这个构造函数应该就是根据@NonNull生成的

实际上我们治理应该添加的注解是NotNull才对

上面因为lombook根据@NonNull生成了一个有参构造函数,导致jdk不会添加默认的无参构造函数,没有无参构造函数的话 序列化就会失败.

@NonNull修饰Field反序列化部分值为空

一般Http接口,为了参数统一管理,定义一个VO用来接收POST过来的字段,常规做法是把参数解析成map,然后反序列化到VO中,早期定义的接口字段都非空,所以VO中都加了@NonNull注解;一直很和谐;

因为需求变化,接口字段需要增加两个,为了版本兼容,新加的两个字段需要可空;于是在VO中增加两个字段,不用@NonNull修饰,但是反序列化后发现这两个字段一直为空!怎么都不能从map中获取到这两个值!

分析

版本:

  • 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

发现其是作用于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",
        }));

再看下经过注解后编译的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() + ")";
    }
}

重点是看这个编译后的class的构造方法:只有一个带@NonNull注解参数的构造方法!!!

一般到这里都能想到反序列化后的为啥另外两个未注解NonNull的为啥空值了;如果没想到,也没关系,咱们再来看看JSON反序列化的过程

2: json反序列化;

一系列递进过程不再描述,重点看JavaBeanInfo类中的build方法,这个方法是真正把map反序化到javaBean的过程

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

挑几处重要的开看下:

取构造方法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());
            }
        }

赋值创建javaBean的构造

   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;
                                    }
                                }

创建javaBean,看传参; 只有一个构造方法;

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

结论:

使用@NonNull注解,编译后生成的CLASS构造方法只有一个,且只有被注解的字段才能构造时候赋值;此种做法是保证在编译期可以判断非空;

反序列化时候使用了这个构造方法,其他的值没有被赋值;

建议改进

1: 使用@NotNull代替

2: 如果修饰的是String类型,推荐使用@NotBlank,好处是可以判断空字符串

3: 在自定义的VO中增加一个无参构造方法;

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

@NonNull导致无法序列化的问题及解决

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

@NonNull导致无法序列化的问题及解决

这篇文章主要介绍了@NonNull导致无法序列化的问题及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
2023-01-06

@NonNull导致无法序列化如何解决

这篇文章主要介绍“@NonNull导致无法序列化如何解决”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“@NonNull导致无法序列化如何解决”文章能帮助大家解决问题。@NonNull导致无法序列化的
2023-07-04

导致mysqld无法启动的一个错误问题及解决

目录导致mysqld无法启动的一个错误mysql 启动报错问题总结导致mysqld无法启动的一个错误由于不小心删除了 /var/log/mysql/ 这个目录(这是mysql服务器用来保存log的地方),导致无法启动mysqld。在终端
2023-02-20

导致mysqld无法启动的错误问题如何解决

这篇文章主要介绍了导致mysqld无法启动的错误问题如何解决的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇导致mysqld无法启动的错误问题如何解决文章都会有所收获,下面我们一起来看看吧。导致mysqld无法启
2023-07-05

Apache2配置问题导致PHP无法解析解决方案

Apache2配置问题导致PHP无法解析解决方案在搭建网站时,经常会遇到Apache2配置问题导致PHP无法解析的情况,这时候我们需要对Apache2的配置进行仔细检查和修改,以确保PHP能够正确解析。本文将详细介绍Apache2配置中可
Apache2配置问题导致PHP无法解析解决方案
2024-03-08

fastjson全局日期序列化设置导致JSONField失效问题解决方案

这篇文章主要介绍了fastjson通过代码指定全局序列化返回时间格式,导致使用JSONField注解标注属性的特殊日期返回格式失效问题的解决方案
2023-01-02

Redis Hash序列化存储的问题及解决方案

目录SDR序列化方式有多种对Redis的存储设置是我自己写的更改序列化方法更改序列化方式继续使用JdkSerializationRedisSerializer这里说的是Spring Data Redis(一下简称SDR)设置Hash存储的序
2022-11-19

流读取导致StringBuilder.toString()乱码的问题及解决

这篇文章主要介绍了流读取导致StringBuilder.toString()乱码的问题及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
2022-11-13

Gojson反序列化“null“的问题解决

本文主要介绍了Gojson反序列化“null“的问题解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
2023-03-14

SpringBoot之Json的序列化和反序列化问题怎么解决

这篇文章主要讲解了“SpringBoot之Json的序列化和反序列化问题怎么解决”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“SpringBoot之Json的序列化和反序列化问题怎么解决”吧
2023-07-02

Java System#exit无法退出程序的问题及解决

这篇文章主要介绍了Java System#exit无法退出程序的问题及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
2023-05-14

因为路由所导致的故障及其解决问题

  路由(routing)是指分组从源到目的地时,决定端到端路径的网络范围的进程。路由工作在OSI参考模型第三层--网络层的数据包转发设备。路由器通过转发数据包来实现网络互连。今天小编要给大家分享一篇教程,那就是:因为路由所导致的故障及其解决问题。  一、故障发生  小编所在公司在全国每一个地方都有分公司的,网络主要就
因为路由所导致的故障及其解决问题
2024-04-18

BaseAdapter导致notifyDataSetChanged()无效的三个原因及解决方法

BaseAdapter导致notifyDataSetChanged()无效的三个原因及解决方法:1. 数据源没有改变:notifyDataSetChanged()只有在数据源改变时才会刷新列表,如果数据源没有改变,调用该方法是无效的。解决方
2023-09-12

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录