此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Framework 6.2.6! |
Null 安全
尽管 Java 不允许你用它的类型系统来表达空安全,但 Spring 框架代码库用 JSpecify 注释来声明 API、字段和相关类型的空性 用法。强烈建议阅读 JSpecify 用户指南,以便获取 熟悉这些注释和语义。
这种显式 null 安全安排的主要目标是防止NullPointerException
在运行时通过
构建时检查,并将显式 null 转换为一种表示可能缺少值的方式。它可用于
通过利用一些工具(NullAway 或支持 null 安全的 IDE)的 Java
注解(如 IntelliJ IDEA 或 Eclipse)和 Kotlin,其中 JSpecify 注解会自动转换为 Kotlin 的 null 安全性。
这Nullness
Spring API可以在运行时用于检测
Type usage、字段、方法返回类型或参数。它完全支持 JSpecify 注解、
Kotlin null 安全性、Java 原语类型以及对任何@Nullable
注解(无论
包)。
使用 JSpecify 注释对库进行注释
从 Spring Framework 7 开始,Spring Framework 代码库利用 JSpecify 注释来公开空安全的 API 和 检查这些 null 安全声明的一致性,其中 NullAway 作为 它的构建。建议根据 Spring Framework(Spring portfolio 项目)对每个库进行 以及与 Spring 生态系统相关的其他库(Reactor、Micrometer 和 Spring 社区项目)一起执行 相同。
在 Spring 应用程序中利用 JSpecify 注释
使用支持 null 安全注释的 IDE(例如 IntelliJ IDEA 或 Eclipse)开发应用程序将提供
当不遵守 null-safety 协定时,Java 中的警告和 Kotlin 中的错误,从而允许 Spring 应用程序
开发人员优化其 null 处理以防止NullPointerException
在运行时抛出。
或者,Spring 应用程序开发人员可以注释他们的代码库,并使用 NullAway 在构建时在应用程序级别强制执行 null 安全。
指引
本节的目的是分享一些为明确指定 Spring 相关的 null 性而提出的准则 库或应用程序。
JS指定
需要了解的关键点是,默认情况下,类型的 null 性在 Java 中是未知的,并且非 null 类型
用法比可为 null 的用法更频繁。为了保持代码库的可读性,我们通常希望定义
默认情况下,类型用法为非 null,除非对于特定范围标记为可为 null。这正是@NullMarked
通常使用 Spring 设置
在软件包级别通过package-info.java
文件,例如:
@NullMarked
package org.springframework.core;
import org.jspecify.annotations.NullMarked;
在属于包的各种 Java 文件中,可为 null 的类型用法是使用@Nullable
.建议将此
annotation 的 intent 指定在 related type 之前。
例如,对于字段:
private @Nullable String fileEncoding;
或者对于方法参数和返回值:
public static @Nullable String buildMessage(@Nullable String message,
@Nullable Throwable cause) {
// ...
}
重写方法时,不会从超类方法继承 null 注释。这意味着那些 如果您只想覆盖实现并保持相同的 API,则应重复 nullness 注释 nullness 的 null 值。
使用数组和 varargs,您需要能够区分元素的 null 性与 数组本身。注意 Java 规范定义的语法,可能是 最初令人惊讶:
-
@Nullable Object[] array
表示单个元素可以为 null,但数组本身不能。 -
Object @Nullable [] array
表示单个元素不能为 null,但数组本身可以。 -
@Nullable Object @Nullable [] array
表示单个元素和数组都可以为 null。
Java 规范还强制要求使用@Target(ElementType.TYPE_USE)
如 JSpecify@Nullable
应在最后一个.
使用内部或完全限定类型:
-
Cache.@Nullable ValueWrapper
-
jakarta.validation.@Nullable Validator
@NonNull
和@NullUnmarked
很少需要
典型用例。
NullAway (空消失)
配置
建议的配置为:
-
NullAway:OnlyNullMarked=true
为了仅对@NullMarked
. -
NullAway:CustomContractAnnotations=org.springframework.lang.Contract
这使得 NullAway @Contract知道org.springframework.lang
package 中 可用于表达互补语义,以避免在代码库中出现不相关的 null 安全警告。
一个很好的例子@Contract
好处是Assert#notnull
它被注释
跟@Contract("null, _ → fail")
.通过上面的配置,NullAway 会明白,在成功的
ininvokecation 时,作为参数传递的值不为 null。
或者,可以将NullAway:JSpecifyMode=true
启用对完整 JSpecify 语义的检查,包括
泛型类型。请注意,此模式仍在开发中,并且需要
使用 JDK 22 或更高版本(通常与--release
Java 编译器标志来配置
预期基线)。建议仅在第二步启用 JSpecify 模式,在确保代码库
使用上述建议的配置不会生成警告。
警告抑制
在一些有效的用例中,NullAway 会错误地检测到 null 性问题。在这种情况下,建议 要禁止显示相关警告并记录原因,请执行以下作:
-
@SuppressWarnings("NullAway.Init")
在 field、constructor 或 class 级别可用于避免不必要的警告 由于字段的延迟初始化,例如由于类实现InitializingBean
. -
@SuppressWarnings("NullAway") // Dataflow analysis limitation
可以在 NullAway 数据流分析不是时使用 能够检测到涉及 null 问题的 path 永远不会发生。 -
@SuppressWarnings("NullAway") // Lambda
当 NullAway 不考虑执行的断言时,可以使用 在 Lambda 之外,用于 Lambda 中的代码路径。 -
@SuppressWarnings("NullAway") // Reflection
可用于一些已知的 return 非 null 值,即使 API 无法表示。 -
@SuppressWarnings("NullAway") // Well-known map keys
可用于Map#get
调用是使用已知的键完成的 存在,并且之前插入了非 null 相关值。 -
@SuppressWarnings("NullAway") // Overridden method does not define nullness
可以在 super class 执行 不定义 null 性(通常当超类来自依赖项时)。
从 Spring 空安全注释迁移
Spring 空安全注释@Nullable
,@NonNull
,@NonNullApi
和@NonNullFields
在org.springframework.lang
包已被
在 Spring Framework 5 中引入,当时 JSpecify 不存在,最好的选择是利用 JSR 305(休眠的
但广泛使用的 JSR)元注解。从 Spring Framework 7 开始,它们已被弃用,取而代之的是 JSpecify 注释,它提供了重要的增强功能,例如正确
定义的规范、没有拆分包问题的规范依赖项、更好的工具、更好的 Kotlin 集成
以及为更多使用案例更精确地指定 null 性的能力。
一个关键的区别是,遵循 JSR 305 语义的 Spring 空安全注释适用于字段, 参数和返回值,而 JSpecify 注释适用于类型用法。这种细微的差异 在实践中非常重要,因为它允许将元素的 null 性与 数组/vararg 的 null性以及定义泛型类型的 null 性。
这意味着 array 和 varargs 空安全声明必须更新以保持相同的语义。例如@Nullable Object[] array
替换为 Spring 注解需要更改为Object @Nullable [] array
使用 JSpecify
附注。varargs 也是如此。
此外,还建议将字段和返回值注释移到更靠近类型的位置,例如:
-
对于字段,而不是
@Nullable private String field
对于 Spring 注解,请使用private @Nullable String field
使用 JSpecify 注释。 -
对于返回值,而不是
@Nullable public String method()
对于 Spring 注解,请使用public @Nullable String method()
使用 JSpecify 注释。
此外,使用 JSpecify,您无需指定@NonNull
当覆盖@Nullable
在
super 方法在标记为 null 的代码中“撤消”可为 null 的声明。只需声明它未注释,并将 null 标记为
defaults(除非显式注释为 nullable,否则类型用法被视为非 null)将适用。