此版本仍在开发中,尚不被认为是稳定的。对于最新的稳定版本,请使用 Spring Framework 6.2.10! |
零安全
尽管 Java 还不允许您使用其类型系统来表达空标记,但 Spring Framework 代码库是 用 JSpecify 注释来声明其 API 的可空性, 字段和相关类型用法。阅读 JSpecify 用户指南非常 推荐,以便熟悉这些注释和语义。
这种空安全安排的主要目标是防止NullPointerException
避免被扔
运行时,并使用显式可空性作为表达可能缺少值的一种方式。
通过利用一些工具(NullAway 或支持
JSpecify 注解,例如 IntelliJ IDEA)和 Kotlin,其中 JSpecify 注解会自动转换为 Kotlin 的 null 安全性。
这Nullness
弹簧 API可以在运行时用于检测
类型用法、字段、方法返回类型或参数的空值。它为
JSpecify 注释、Kotlin null 安全性和 Java 基元类型,以及对任何@Nullable
注释(无论包如何)。
使用 JSpecify 注释注释库
从 Spring Framework 7 开始,Spring Framework 代码库利用 JSpecify 注释来公开空安全 API 并在其生成过程中检查这些可空性声明与 NullAway 的一致性。根据 Spring Framework 和 Spring Portfolio 项目,建议用于每个库, 以及与 Spring 生态系统相关的其他库(Reactor、Micrometer 和 Spring 社区项目), 做同样的事情。
在 Spring 应用程序中利用 JSpecify 注释
使用支持空注解的 IDE 开发应用程序将在 Java 中提供警告,并在
Kotlin 当不遵守可空性契约时,允许 Spring 应用程序开发人员改进其
null 处理以防止NullPointerException
避免在运行时抛出。
或者,Spring 应用程序开发人员可以注释他们的代码库,并使用 NullAway 等构建插件在构建期间在应用程序级别强制执行 null 安全。
指引
本节的目的是分享一些建议的准则,用于明确指定 与 Spring 相关的库或应用程序。
JSpecify
默认为非空
需要理解的一个关键点是,在 Java 中,默认情况下类型的空性是未知的,并且非空类型的用法
比可为空的用法更频繁。为了保持代码库的可读性,我们通常希望通过
默认类型用法为非 null,除非标记为特定范围的可为 null。这正是目的
之@NullMarked
通常设置
在 Spring 项目中,通过package-info.java
文件,例如:
@NullMarked
package org.springframework.core;
import org.jspecify.annotations.NullMarked;
显式可空性
在@NullMarked
代码中,可为 null 类型的用法是显式定义的@Nullable
.
JSpecify 之间的主要区别@Nullable
/ @NonNull
注释和大多数其他变体是 JSpecify
注释是用@Target(ElementType.TYPE_USE)
,因此它们仅适用于类型用法。这会影响
应该放置此类注释的位置,以符合相关的 Java 规范或遵循代码
样式最佳实践。从样式的角度来看,建议接受这些注释的类型使用性质
通过将它们放置在与带注释的类型相同的行上,并紧接在注释类型之前。
例如,对于字段:
private @Nullable String fileEncoding;
或者对于方法参数和方法返回类型:
public @Nullable String buildMessage(@Nullable String message,
@Nullable Throwable cause) {
// ...
}
重写方法时,JSpecify 注释不会从原始 方法。这意味着如果出现以下情况,则应将 JSpecify 注释复制到覆盖方法中 你想要重写实现并保持相同的可空性语义。 |
@NonNull
和@NullUnmarked
应该很少需要
典型用例。
数组和变量
使用数组和变量,您需要能够区分元素的空性与
数组本身。注意 Java 规范定义的语法,它可能是
最初令人惊讶。例如,在@NullMarked
法典:
-
@Nullable Object[] array
表示单个元素可以null
但数组本身不能。 -
Object @Nullable [] array
意味着单个元素不能null
但数组本身可以。 -
@Nullable Object @Nullable [] array
表示单个元素和数组都可以是null
.
泛 型
JSpecify 注释也适用于泛型。例如,在@NullMarked
法典:
-
List<String>
表示非空元素的列表(相当于List<@NonNull String>
) -
List<@Nullable String>
表示可为 null 元素的列表
当您声明泛型类型或泛型方法时,事情会稍微复杂一些。有关更多详细信息,请参阅相关的 JSpecify 泛型文档。
NullAway 尚未完全支持泛型类型和泛型方法的可空性。 |
空虚
配置
推荐的配置是:
-
NullAway:OnlyNullMarked=true
为了仅对标有@NullMarked
. -
NullAway:CustomContractAnnotations=org.springframework.lang.Contract
这使得 NullAway @Contract知道org.springframework.lang
包,其中 可用于表达互补语义,以避免代码库中出现不相关的警告。
一个很好的例子@Contract
声明可以看到Assert.notNull()
其中注释为@Contract("null, _ → fail")
.通过该合约声明,NullAway 将了解
成功调用Assert.notNull()
.
或者,可以设置NullAway:JSpecifyMode=true
启用对完整 JSpecify 语义的检查,包括对
数组、vararg 和泛型。请注意,此模式仍在开发中,需要
JDK 22 或更高版本(通常与--release
Java 编译器标志来配置
预期基线)。建议仅在第二步中启用 JSpecify 模式,在确保代码库
不会使用本节前面提到的推荐配置生成警告。
警告抑制
在一些有效的用例中,NullAway 会错误地检测到可空性问题。在这种情况下, 建议禁止显示相关警告并记录原因:
-
@SuppressWarnings("NullAway.Init")
在字段、构造函数或类级别都可以用来避免不必要的警告 由于字段的延迟初始化——例如,由于实现的类InitializingBean
. -
@SuppressWarnings("NullAway") // Dataflow analysis limitation
当 NullAway 数据流分析不是 能够检测到涉及可空性问题的路径永远不会发生。 -
@SuppressWarnings("NullAway") // Lambda
当 NullAway 不考虑执行的断言时可以使用 在 lambda 之外,用于 lambda 内的代码路径。 -
@SuppressWarnings("NullAway") // Reflection
可用于一些已知返回的反射作 非空值,即使 API 无法表示。 -
@SuppressWarnings("NullAway") // Well-known map keys
可以在以下情况下使用Map#get
调用是使用键执行的 已知存在的值,以及之前插入了非空相关值时。 -
@SuppressWarnings("NullAway") // Overridden method does not define nullability
可以在超类 不定义可空性(通常当超类来自外部依赖项时)。 -
@SuppressWarnings("NullAway") // See github.com/uber/NullAway/issues/1075
当 NullAway 无法检测泛型方法中的类型变量空时,可以使用。
从 Spring 空安全注解迁移
Spring 空安全注释@Nullable
,@NonNull
,@NonNullApi
和@NonNullFields
在org.springframework.lang
包裹是
在 Spring Framework 5 中引入时,JSpecify 还不存在,当时最好的选择是利用
来自 JSR 305(休眠但广泛使用的 JSR)的元注释。从 Spring Framework 7 开始,它们已被弃用,取而代之的是 JSpecify 注释,它提供了显着的增强功能,例如
定义的规范、没有拆分包问题的规范依赖项、更好的工具、更好的 Kotlin 集成、
以及为更多用例更精确地指定可空性的能力。
一个关键的区别是,Spring 已弃用的空安全注释(遵循 JSR 305 语义)适用于字段, 参数和返回值;而 JSpecify 注释适用于类型用法。这种细微的区别 在实践中非常重要,因为它允许开发人员区分元素的空性和 数组/varargs 的空性以及定义泛型类型的空性。
这意味着必须更新数组和 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
在超级方法中“撤消”标记为 null 的代码中的可为 null 声明。只需声明它未注释,并且
将应用标记为 null 的默认值(类型用法被视为非 null,除非显式注释为可为 null)。