@NotNull与@NotBlank:非空校验你用对了吗?💞

1,305 阅读10分钟

🏆本文收录于「滚雪球学SpringBoot」专栏,专业攻坚指数级提升持续更新中,up!up!up!!

📜 前言

  嘿,家人们!前两期我专门为同事写了两篇关于非空校验的文章,结果她特别客气,直接表示要请我吃饭感谢我。你们说,这顿饭我是去还是不去呢?要是我去了,这篇文章就没法写了;但如果这篇文章发出来了,那就说明我没去……哈哈!不过,话说回来,如果你也常常在表单数据校验中搞不清 @NotNull@NotBlank 的区别,那么这篇文章绝对是为你量身打造的!

  😄 在数据校验中,非空检查是第一步,保证输入数据的完整性是开发的基本要求。然而,@NotNull@NotBlank 这两个“非空守护者”看起来非常相似,实则各有千秋。如果用错了,很可能会引发一些意想不到的校验问题,就像我那同事上期制造的“生产事故”一样,险些让人怀疑人生!

  好了,废话不多说,本期内容正式开启!我将深入解析这两个注解的用法、适用场景以及底层实现的差异。除此之外,我还准备了一些小实例,助你快速掌握它们的核心区别,轻松避免踩坑!💪准备好了吗?我要开始咯。

📚 目录

  1. 🧐 @NotNull 与 @NotBlank 的基础概念
  2. 🎩 使用场景与适用类型
  3. ✍️ 实例演示:@NotNull 和 @NotBlank 在代码中的实际应用
  4. 🤔 它们与 @NotEmpty 有什么不同?
  5. 🛠️ 深度解析:底层原理与实现区别
  6. 💡 最佳实践与常见误区
  7. 📈 总结与心得

🧐 @NotNull 与 @NotBlank 的基础概念

  在数据验证中,@NotNull@NotBlank 都是用于保证字段的非空性,但二者的非空性定义却有所不同:

  1. @NotNull:要求字段不能是 null。换句话说,如果一个字段被 @NotNull 标注,那么这个字段在使用时必须存在值,但是允许它是空字符串或空集合(所以说在某些场景下,需要特别注意其区别用法,用错了可就罪过了)。

  2. @NotBlank:要求字段不仅不能是 null,还不能是空字符串,且内容不能全为空格。这意味着,@NotBlank 是一个更严格的验证注解,通常用于字符串类型字段。

  简单来说,@NotNull 注解只是保证字段存在,而 @NotBlank 还会进一步确保字段内容有意义。

🎩 使用场景与适用类型

  虽然 @NotNull@NotBlank 都用于非空检查,但它们的应用场景和适用类型却有明显的区别:

  • @NotNull 适用于任何对象类型,可以用于 StringListMapInteger 等,甚至自定义类的字段。例如,在用户实体类中,如果我们希望 userIdusername 等字段不能为 null,可以用 @NotNull 来标注。

  • @NotBlank 只适用于 String 类型,因为它的检查内容包括非空白字符。@NotBlank 用于确保用户输入的字符串不为空且有实际内容,适合像用户名、密码、电子邮件这类字段。

举例对比

  假设我们有一个 User 类,需要确保用户名、邮箱不为空,年龄可以为空,但如果填写则必须有实际内容,示例代码如下:

import javax.validation.constraints.NotNull;
import javax.validation.constraints.NotBlank;

public class User {

    @NotNull(message = "用户ID不能为空")
    private Integer userId;

    @NotBlank(message = "用户名不能为空")
    private String username;

    @NotNull(message = "年龄不能为空")
    private Integer age;

    // Getter 和 Setter
}

  在这个例子中,userIdage 使用 @NotNull 进行校验检查,而 username 则用 @NotBlank 确保用户输入实际内容。

✍️ 实例演示:@NotNull 和 @NotBlank 在代码中的实际应用

  接下来我们通过一个简单的示例代码,展示 @NotNull@NotBlank 的实际应用效果。

Step 1: 导入对应依赖

  首先,为了使用 @NotNull,你需要在项目中引入一个支持 Jakarta Bean Validation 的实现库,比如 Hibernate Validator。

以下是常见的 Maven 依赖配置:

Maven 配置:

<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.2.5.Final</version> <!-- 请根据需要选择版本 -->
</dependency>
<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <version>2.0.1.Final</version>
</dependency>

Gradle 配置:

implementation 'org.hibernate.validator:hibernate-validator:6.2.5.Final'
implementation 'javax.validation:validation-api:2.0.1.Final'

  这个根据大家具体的项目依赖构建方式而定,而选择对应的方式导入依赖。

Step 2: 创建验证实体类

  假设我们有一个 RegistrationForm,其中 name 必须填写有效字符,email 不能为 nullage 则可以为空但如果填写必须为正数。

import javax.validation.constraints.NotNull;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Positive;

public class RegistrationForm {

    @NotBlank(message = "用户名不能为空")
    private String name;

    @NotNull(message = "邮箱不能为空")
    private String email;

    @Positive(message = "年龄必须为正数")
    private Integer age;

    // Getter 和 Setter
}

Step 3: 在 Controller 中使用 @Validated 触发验证

  在 Spring Boot 项目中,我们可以在 Controller 层使用 @Valid 注解来自动触发验证逻辑:

@RestController
@RequestMapping("/system/testUser")
public class RegistrationController {

    /**
     * 新增user
     */
    @ApiOperation("新增user")
    @PostMapping("/add")
    public R<Void> add(@Validated @RequestBody User user) {
        return toR(userService.insert(user));
    }
}

Step 4: 捕获验证失败异常

  为了更好地处理验证错误,我们可以创建一个全局异常处理器:

import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public String handleValidationExceptions(MethodArgumentNotValidException ex) {
        return ex.getBindingResult().getFieldError().getDefaultMessage();
    }
}

  这样一来,当输入的 name 是空字符串,emailnull 时,系统会根据注解的 message 属性返回对应的错误提示。

Step 5: 实际演示

  为了验证如上代码注解是否真正能够拦截这些情景,我们直接上Postman来模拟调用接口测试一下。

模拟场景1:用户名直接传空值

  这里我们请求的时候,将name直接传null值进行请求。Postman请求如下:

  如上成功验证若用户名传入空值则自动会被拦截,且提示对应的message信息,如“姓名不能为空”。

模拟场景2:邮箱号直接传null值

  继续模拟,这里我们在发请求的时候,将email直接传null值。Postman请求如下:

  如上成功验证邮箱号传null值依然会被拦截,强行提醒“邮箱不能为空”,告知用户其字段信息的校验规则。

模拟场景3:年龄直接传负值

  继续模拟,这里我们在发请求的时候,将age直接传个负数。Postman请求如下:

  如上成功验证年龄传负值也依然会被拦截,强行被提示“年龄必须为正数”。

  总之,这里已经替大家都测试过了,大家可放心食用,根据不同的场景选择合适的校验注解。

🤔 它们与 @NotEmpty 有什么不同?

  除了 @NotNull@NotBlank 之外,@NotEmpty 也是一个常见的校验注解(这个我特地抽一期聊聊),特别是针对集合类时会经常使用。@NotEmpty 的含义介于 @NotNull@NotBlank 之间,通常用于集合类或字符串,要求字段既不为 null,也不能是空集合或空字符串。

三者的区别

  • @NotNull:仅要求字段不为 null,允许空字符串或空集合。
  • @NotEmpty:不仅不为 null,还要求有内容,适用于集合或字符串。
  • @NotBlank:最严格,要求字段不为 null,且内容不为全空格,仅适用于字符串。

🛠️ 深度解析:底层原理与实现区别

  在底层实现上,@NotNull@NotBlank 都依赖于 Java Bean Validation 规范(JSR 380),通常由 Hibernate Validator 提供实现。以下是关于 @NotNull@NotBlank 的底层原理与实现区别的对照:

对比维度@NotNull@NotBlank
基本功能检查对象是否为 null检查字符串是否既非 null 且包含至少一个非空白字符
适用类型任意对象类型,包括 String、集合、数值等仅适用于 String 类型
校验规则只需判断值是否为 null判断值是否为 null
去除首尾空格后判断是否为空字符串
实现类NotNullValidatorNotBlankValidator
核心逻辑使用 value != null 检测是否非空使用 value != null && !value.trim().isEmpty() 检测是否包含有效字符
适用场景确保字段被赋值
常用于对象引用的非空验证
确保字符串非空且有效
常用于用户输入、文本字段等
依赖库Hibernate Validator 或 Bean Validation(JSR 380)Hibernate Validator 或 Bean Validation(JSR 380)
典型应用场景检查实体类中的复杂对象是否非空检查表单输入或字符串字段是否包含有效字符
误用风险无法检测字符串是否是空白字符(例如 " "无法应用于非字符串类型,如集合、数组等

总而言之:

  • 使用 @NotNull 时,只关心值是否为 null,适用于所有对象类型。
  • 使用 @NotBlank 时,不仅要检查非 null,还会去掉空格检查是否为空字符串,专用于字符串校验。

  通过明确两者的差异,可以在实际开发中更精准地选择合适的校验注解,从而避免潜在问题。

💡 最佳实践与常见误区

  1. 针对字段类型选择正确的注解:当字段类型是字符串,且要求非空和非空白内容时使用 @NotBlank,其他对象类型建议使用 @NotNull

  2. 提供清晰的错误信息:在每个注解的 message 属性中设置用户友好的提示内容,这样当验证失败时,用户能得到明确的反馈。

  3. 避免误用:对于非字符串字段(如整数、集合等),不要使用 @NotBlank,它只适用于字符串。使用 @NotNull@NotEmpty 更适合。

  4. 结合使用多个注解:在实际项目中,可能需要更复杂的校验,可以将 @NotNull@Pattern@Size 等注解组合使用,实现更精准的控制。

📈 总结与心得

  说到这儿,咱们就要说再见了,而你们才刚开始。我就简单概括一下:@NotNull@NotBlank 这两是数据校验中非常重要的注解,它们虽然都与非空校验相关,但在实际应用中各有侧重:@NotNull 用于检查任意对象是否为 null,而 @NotBlank 则专门针对字符串,确保其不仅非空,还必须包含至少一个非空白字符,所以大家要根据合适的场景进行使用,切不可乱用。

  正确选择和使用这两个注解,可以有效保障输入数据的完整性与准确性。通过这篇文章,希望大家能更清晰地掌握 @NotNull@NotBlank 的区别,避免在项目开发中踩坑!还没在项目中使用的,快动手在你的项目中尝试一下吧,合理运用这两个注解,助力你少些代码,释放双手,真可谓是谁用谁爽系列呢,OK,本期就说到这儿,咱们下期见🎉

📣 关于我

我是bug菌,CSDN | 掘金 | InfoQ | 51CTO | 华为云 | 阿里云 | 腾讯云 等社区博客专家,C站博客之星Top30,华为云2023年度十佳博主,掘金多年度人气作者Top40,掘金等各大社区平台签约作者,51CTO年度博主Top12,掘金/InfoQ/51CTO等社区优质创作者;全网粉丝合计 30w+;硬核微信公众号「猿圈奇妙屋」,欢迎你的加入!免费白嫖最新BAT互联网公司面试真题、4000G PDF电子书籍、简历模板等海量资料,你想要的我都有,关键是你不来拿哇。

-End-