1. 概述

从 Spring 5 开始,我们迎来了一个非常实用的功能 —— null 安全(null safety)。它由一组注解组成,就像代码的“安全卫士”,帮我们提前发现潜在的 null 引用问题。

⚠️ 与放任不安全代码不同的是,null 安全机制会在编译期就给出警告,从而避免运行时出现灾难性的空指针异常(NPE)。

2. @NonNull 注解

在所有 null 安全相关的注解中,@NonNull 是最核心的一个。✅ 它可以用于任何对象引用的地方,包括字段、方法参数或返回值,用来声明该引用不允许为 null。

举个例子,假设我们有一个 Person 类:

public class Person {
    private String fullName;

    void setFullName(String fullName) {
        if (fullName != null && fullName.isEmpty()) {
            fullName = null;
        }
        this.fullName = fullName;
    }

    // getter
}

这段代码虽然合法,但存在隐患:fullName 字段可能被设置为 null。一旦发生这种情况,在后续使用 fullName 时可能会抛出 NPE。

这时候,如果你用 IntelliJ IDEA 并给 fullName 字段加上 @NonNull 注解,IDE 就会提示你一个警告:

nonnul annotation

通过这个提示,我们可以提前发现问题并做出调整,避免运行时出错。

3. @NonNullFields 注解

虽然 @NonNull 很有用,但如果每个字段都手动加上这个注解,代码会变得冗余不堪。❌

为了解决这个问题,Spring 提供了另一个利器:@NonNullFields。✅ 这个注解作用于包级别,表示当前包下所有的字段默认都不能为 null。

要启用这个注解,我们需要在对应包的根目录下创建一个名为 package-info.java 的文件,并添加如下内容:

@NonNullFields
package org.baeldung.nullibility;

接着我们在 Person 类中新增一个属性 nickName

package org.baeldung.nullibility;

// import statements

public class Person {
    private String nickName;

    void setNickName(@Nullable String nickName) {
        if (nickName != null && nickName.isEmpty()) {
            nickName = null;
        }
        this.nickName = nickName;
    }

    // other declarations
}

此时即使没有给 nickName 加上 @NonNull,IDE 依然会提示类似的警告信息:

nonnullfields annotation 3

✅ 使用 @NonNullFields 能让我们的代码更简洁,同时保持和 @NonNull 一样的安全性。

4. @Nullable 注解

既然有了默认非空的设定(如 @NonNullFields),那如果某些字段确实允许为 null 怎么办?这时候就需要用到 @Nullable 注解。

回到上面的例子,我们给 nickName 字段加上 @Nullable 注解:

@Nullable
private String nickName;

你会发现之前的警告消失了:

nullable annotation

✅ 所以说,**@Nullable 可以覆盖包级的 null 安全策略,灵活地指定某个字段可以为 null。**

5. @NonNullApi 注解

前面提到的 @NonNullFields 只对字段生效。如果我们还想对方法的参数和返回值也做同样的限制呢?

这时候就要请出 @NonNullApi 注解了。✅ 它和 @NonNullFields 类似,也需要在 package-info.java 中声明:

@NonNullApi
package org.baeldung.nullibility;

然后我们给 nickName 添加一个 getter 方法:

package org.baeldung.nullibility;

// import statements

public class Person {
    @Nullable
    private String nickName;

    String getNickName() {
        return nickName;
    }

    // other declarations
}

由于启用了 @NonNullApi,IDE 会提示 getNickName() 方法可能返回 null:

nonnullapi annotation

✅ 同样地,你也可以在方法上使用 @Nullable 来覆盖包级别的 null 约束。

6. 总结

Spring 的 null 安全机制是一套非常实用的工具,能显著降低 NPE 发生的概率。不过有两点需要注意:

❌ 它只在支持的开发工具中有效(比如 IntelliJ IDEA)
❌ 它不会在运行期强制检查 null —— 也就是说,最终防止 NPE 还得靠你自己写好代码

本教程的完整源码可以在 GitHub 上找到。


原始标题:Spring Null-Safety Annotations | Baeldung