lombok@NonNull 注解无法对 SpringMVC 封装请求的对象的 Field 进行非空校验

java;gutter:true; If put on a parameter, lombok will insert a null-check at the start of the method / constructor's body, throwing a {@code NullPointerException} with the parameter's name as message. If put on a field, any generated method assigning a value to this field will also produce these null-checks.</p> <pre><code> 可见 @NonNull 注解用于标注Field时,并非是直接校验所标注字段不能为空,而是对赋值该字段的方法加 null 校验。 如该对象 ;gutter:true;
@Data
@AllArgsConstructor
public class ReqIn {

@NonNull
private Byte name;

private String id;

public ReqIn(){
System.out.println("无参构造");
}

}

编译生成 .class 文件如下

java;collapse:true;;gutter:true; public class ReqIn { @NonNull private String name; private String id;</p> <pre><code>public ReqIn() { System.out.println("无参构造"); } @NonNull public String getName() { return this.name; } public String getId() { return this.id; } public void setName(@NonNull final String name) { if (name == null) { throw new NullPointerException("name is marked non-null but is null"); } else { this.name = name; } } public void setId(final String id) { this.id = id; } public boolean equals(final Object o) { if (o == this) { return true; } else if (!(o instanceof ReqIn)) { return false; } else { ReqIn other = (ReqIn)o; if (!other.canEqual(this)) { return false; } else { Object this$name = this.getName(); Object other$name = other.getName(); if (this$name == null) { if (other$name != null) { return false; } } else if (!this$name.equals(other$name)) { return false; } Object this$id = this.getId(); Object other$id = other.getId(); if (this$id == null) { if (other$id != null) { return false; } } else if (!this$id.equals(other$id)) { return false; } return true; } } } protected boolean canEqual(final Object other) { return other instanceof ReqIn; } public int hashCode() { int PRIME = true; int result = 1; Object $name = this.getName(); int result = result * 59 + ($name == null ? 43 : $name.hashCode()); Object $id = this.getId(); result = result * 59 + ($id == null ? 43 : $id.hashCode()); return result; } public String toString() { return "ReqIn(name=" + this.getName() + ", id=" + this.getId() + ")"; } public ReqIn(@NonNull final String name, final String id) { if (name == null) { throw new NullPointerException("name is marked non-null but is null"); } else { this.name = name; this.id = id; } } </code></pre> <p>}</p> <pre><code> 可见,对 setName() 和 @AllArgsConstructor 注解生成的全参数构造器,均对 name 入参做非 null 校验。 但当 @Controller 内 @RequestMapping 注解的接口,若以 ReqIn 作为封装的请求对象,则接口调用时若没有传 name 字段,则会发现接口接收到的入参里,虽然字段被标注了 @NonNull,但 name 确实为 null。 原因简单总述为:SpringMVC对参数解析和封装对象时,对于有无参构造器的对象,会使用无参构造器初始化,随后对传入的 Field 进行赋值,对没有传的 Field 则会忽略。因此,虽然带有 name 参数的构造器和 setName()均对 name 做了 null 校验,但实际上根本没有被调用。 A)初始化被封装对象 ![lombok@NonNull 注解无法对 SpringMVC 封装请求的对象的 Field 进行非空校验](https://johngo-pic.oss-cn-beijing.aliyuncs.com/articles/20230605/1575404-20220726160802163-746005591.png) 详述: 1、SpringMVC对参数解析和封装对象时 如果目标对象仅有一个 Constructor,则会调用该 Constructor 封装对象; 如果对象有多于一个 Constructor,则会取无参构造器来封装对象,本例中无参构造器为手动生成,使用 @NoArgsConstructor 同样效果。 如果对象没有指定构造器,这种情况在有 @NonNull 注解时不存在,因为 @NonNull 注解会对指定了该注解的 Field 生成 Constructor,参数与被注解字段数量一致。 ![lombok@NonNull 注解无法对 SpringMVC 封装请求的对象的 Field 进行非空校验](https://johngo-pic.oss-cn-beijing.aliyuncs.com/articles/20230605/1575404-20220726153501988-1465625439.png) 2、如果被 @NonNull 注解的字段为 String,则注解不会生效。 ;gutter:true;
@Data
@AllArgsConstructor
public class ReqIn {

@NonNull
private String name;

@NonNull
private String id;

public ReqIn(){
System.out.println("无参构造");
}
}

@RequestMapping("/req")@Controllerpublic class ReqInController {    @GetMapping("/testAnnotation")    public String testAnnotation(ReqIn reqIn){        System.out.println(reqIn);        return "index";    }}

java;gutter:true;</p> <pre><code> 2.1、如果请求时,带有 name 字段,则 name 字段会被设置为空字符串 ;gutter:true;
请求:http://localhost:1234/req/testAnnotation?id=133&name=

java;gutter:true; 输出:无参构造 ReqIn(name=, id=133)</p> <pre><code> 2.2、如果请求时,不带有 name 字段,则 name 字段会被设置为 null ;gutter:true;
请求:http://localhost:1234/req/testAnnotation?id=133

java;gutter:true;
输出:无参构造
ReqIn(name=null, id=133)

可见,对于 String 字段,在 SpringMVC 封装对象时,@NonNull 注解无法阻止字段被设置为null。

原因在于,调用无参构造器之后,由于没有传入 name,封装对象的 parameterTypes 仅有 id,进而没有调用过 setName()方法,@NonNull 注解对 name 字段自然无法约束非 null。

结论

要使 @NonNull 注解对于指定 Field 生效,则需要

1、被封装对象没有无参构造器

2、如果被注解 Field 是 String 类型,则如果该参数为空时,不要作为请求的 key-value 对的 key 传到后端接口。

Original: https://www.cnblogs.com/wanghuanyeah/p/16520379.html
Author: wanghuanyeah
Title: lombok@NonNull 注解无法对 SpringMVC 封装请求的对象的 Field 进行非空校验

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/590195/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • Spring5.0学习知识总结

    spring总结 1 、spring 1.1 spring 简介 简化了项目的开发、但配置依然很繁琐!!!扩展: SSH:Struct2 + Spring + Hibernate …

    Java 2023年6月14日
    072
  • 【SpringCloud原理】Ribbon核心组件以及运行原理万字源码剖析

    系列文章还在持续更新,如有喜欢的小伙伴可以关注微信公众号 三友的java日记 一、Ribbon的核心组件 1、Server 这是个很简单的东西,就是服务实例数据的封装,里面封装了服…

    Java 2023年6月16日
    070
  • 各种java面试题目

    地址:https://www.cnblogs.com/crazymakercircle/p/14365820.html 月薪过5万 面试题 总目录 搞定下面这些面试题,2021春招…

    Java 2023年5月29日
    076
  • 字符串中实用的方法

    1、strtok()用来将字符串分割成一个个片段。参数s指向欲分割的字符串,参数delim则为分割字符串,当strtok()在参数s的字符串中发现到参数delim的分割字符时则会将…

    Java 2023年5月29日
    092
  • 【java并发核心一】Semaphore 的使用思路

    最近在看一本书《Java并发编程 核心方法与框架》,打算一边学习一边把学习的经验记下来,所粘贴的代码都是我运行过的,大家一起学习,欢迎吐槽。 估计也没多少人看我的博客,哈哈,那么我…

    Java 2023年5月29日
    0105
  • java.net.NoRouteToHostException: 没有到主机的路由

    今天在配置Jenkins 的云服务器的时候提示:java.net.NoRouteToHostException: 没有到主机的路由,网上查到的没有主机路由问题提到的大多是防火墙问题…

    Java 2023年5月29日
    070
  • 太极限了,JDK的这个BUG都能被我踩到

    hello,大家好呀,我是小楼。 之前遇到个文件监听变更的问题,刚好这周末有空研究了一番,整理出来分享给大家。 从一次故障说起 我们还是从故障说起,这样更加贴近实际,也能让大家更快…

    Java 2023年6月6日
    0128
  • 具有timeout 功能的函数调用

    做项目的时候有时经常会需要一个带有timeout功能的函数调用。 比如从后台读数据并期望在给定时间内返回。借此机会包装了一个简单的C# class, 直接上代码吧. public …

    Java 2023年6月5日
    080
  • 错误记录:Can’t connect to MySQL server on xxx

    由于之前Django项目的mysql的3306端口直接映射到宿主机的3306端口最近安装的其他服务, 发现3306端口冲突, 就把原本Django项目的mysql服务的端口修改, …

    Java 2023年6月7日
    0108
  • Stream流式计算

    Stream流式计算 集合/数据库用来进行数据的存储而计算则交给流 undefined public class Demo { public static void main(St…

    Java 2023年6月5日
    0103
  • 获取数组子序列

    /** * 位对应法 * * @param array */ public void place(int[] array) { //获取数据长度 那么对应的就是二进制字节位数1存在…

    Java 2023年6月5日
    057
  • Day5

    今天上了一天的课,还有测评综合素质,所以今天没有敲代码,但是今天的黑眼圈好严重 Original: https://www.cnblogs.com/tomn/p/16142704….

    Java 2023年6月5日
    090
  • vscode中编写node.js代码的提示功能

    1.安装typings包 安装命令 // &#x4F7F;&#x7528;cnpm,&#x524D;&#x63D0;&#x914D;&amp…

    Java 2023年6月15日
    076
  • 本文章内容主要摘选自《Java核心技术卷II》 是一种概念,也可以看作一种管道,连接着内存与硬盘(或其他媒介) 相对程序而言,输入流是把外部媒介的数据传输到内存的对象,而输出则是到…

    Java 2023年6月5日
    065
  • MySQL搭建主从集群详细步骤~

    404. 抱歉,您访问的资源不存在。 可能是网址有误,或者对应的内容被删除,或者处于私有状态。 代码改变世界,联系邮箱 contact@cnblogs.com 园子的商业化努力-困…

    Java 2023年6月7日
    064
  • AES php java 互转

    php 注意:php 的 mcrypt_簇 在 7.1.0 版本中开始 deprecated,并在 7.2.0 版本中彻底废弃 ,可以增加@来抑制报错 mcrypt always …

    Java 2023年5月29日
    092
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球