文章详情

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

请输入下面的图形验证码

提交验证

短信预约提醒成功

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

2023-07-04 23:04

关注

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

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

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

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

将.class反编译

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

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

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

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

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

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

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

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

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

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

分析

版本:

原代码

package com.example.demo;import lombok.Data;import lombok.NonNull;@Datapublic 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: 注解提供者问题

发现其是作用于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)@Documentedpublic @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构造方法只有一个,且只有被注解的字段才能构造时候赋值;此种做法是保证在编译期可以判断非空;

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

建议改进

使用@NotNull代替

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

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

关于“@NonNull导致无法序列化如何解决”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识,可以关注编程网行业资讯频道,小编每天都会为大家更新不同的知识点。

阅读原文内容投诉

免责声明:

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

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

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯