此版本仍在开发中,尚不被认为是稳定的。对于最新的稳定版本,请使用 Spring Framework 6.2.10spring-doc.cadn.net.cn

零安全

尽管 Java 还不允许您使用其类型系统来表达空标记,但 Spring Framework 代码库是 用 JSpecify 注释来声明其 API 的可空性, 字段和相关类型用法。阅读 JSpecify 用户指南非常 推荐,以便熟悉这些注释和语义。spring-doc.cadn.net.cn

这种空安全安排的主要目标是防止NullPointerException避免被扔 运行时,并使用显式可空性作为表达可能缺少值的一种方式。 通过利用一些工具(NullAway 或支持 JSpecify 注解,例如 IntelliJ IDEA)和 Kotlin,其中 JSpecify 注解会自动转换为 Kotlin 的 null 安全性spring-doc.cadn.net.cn

Nullness弹簧 API可以在运行时用于检测 类型用法、字段、方法返回类型或参数的空值。它为 JSpecify 注释、Kotlin null 安全性和 Java 基元类型,以及对任何@Nullable注释(无论包如何)。spring-doc.cadn.net.cn

使用 JSpecify 注释注释库

从 Spring Framework 7 开始,Spring Framework 代码库利用 JSpecify 注释来公开空安全 API 并在其生成过程中检查这些可空性声明与 NullAway 的一致性。根据 Spring Framework 和 Spring Portfolio 项目,建议用于每个库, 以及与 Spring 生态系统相关的其他库(Reactor、Micrometer 和 Spring 社区项目), 做同样的事情。spring-doc.cadn.net.cn

在 Spring 应用程序中利用 JSpecify 注释

使用支持空注解的 IDE 开发应用程序将在 Java 中提供警告,并在 Kotlin 当不遵守可空性契约时,允许 Spring 应用程序开发人员改进其 null 处理以防止NullPointerException避免在运行时抛出。spring-doc.cadn.net.cn

或者,Spring 应用程序开发人员可以注释他们的代码库,并使用 NullAway 等构建插件在构建期间在应用程序级别强制执行 null 安全。spring-doc.cadn.net.cn

指引

本节的目的是分享一些建议的准则,用于明确指定 与 Spring 相关的库或应用程序。spring-doc.cadn.net.cn

JSpecify

默认为非空

需要理解的一个关键点是,在 Java 中,默认情况下类型的空性是未知的,并且非空类型的用法 比可为空的用法更频繁。为了保持代码库的可读性,我们通常希望通过 默认类型用法为非 null,除非标记为特定范围的可为 null。这正是目的 之@NullMarked通常设置 在 Spring 项目中,通过package-info.java文件,例如:spring-doc.cadn.net.cn

@NullMarked
package org.springframework.core;

import org.jspecify.annotations.NullMarked;

显式可空性

@NullMarked代码中,可为 null 类型的用法是显式定义的@Nullable.spring-doc.cadn.net.cn

JSpecify 之间的主要区别@Nullable / @NonNull注释和大多数其他变体是 JSpecify 注释是用@Target(ElementType.TYPE_USE),因此它们仅适用于类型用法。这会影响 应该放置此类注释的位置,以符合相关的 Java 规范或遵循代码 样式最佳实践。从样式的角度来看,建议接受这些注释的类型使用性质 通过将它们放置在与带注释的类型相同的行上,并紧接在注释类型之前。spring-doc.cadn.net.cn

例如,对于字段:spring-doc.cadn.net.cn

private @Nullable String fileEncoding;

或者对于方法参数和方法返回类型:spring-doc.cadn.net.cn

public @Nullable String buildMessage(@Nullable String message,
                                     @Nullable Throwable cause) {
    // ...
}

重写方法时,JSpecify 注释不会从原始 方法。这意味着如果出现以下情况,则应将 JSpecify 注释复制到覆盖方法中 你想要重写实现并保持相同的可空性语义。spring-doc.cadn.net.cn

@NonNull@NullUnmarked应该很少需要 典型用例。spring-doc.cadn.net.cn

数组和变量

使用数组和变量,您需要能够区分元素的空性与 数组本身。注意 Java 规范定义的语法,它可能是 最初令人惊讶。例如,在@NullMarked法典:spring-doc.cadn.net.cn

泛 型

JSpecify 注释也适用于泛型。例如,在@NullMarked法典:spring-doc.cadn.net.cn

当您声明泛型类型或泛型方法时,事情会稍微复杂一些。有关更多详细信息,请参阅相关的 JSpecify 泛型文档spring-doc.cadn.net.cn

NullAway 尚未完全支持泛型类型和泛型方法的可空性。

嵌套类型和完全限定类型

Java 规范还强制使用@Target(ElementType.TYPE_USE)– 就像 JSpecify 的@Nullable注释 – 必须在最后一个点 (.) 在内部或完全限定的类型名称中:spring-doc.cadn.net.cn

空虚

配置

推荐的配置是:spring-doc.cadn.net.cn

  • NullAway:OnlyNullMarked=true为了仅对标有@NullMarked.spring-doc.cadn.net.cn

  • NullAway:CustomContractAnnotations=org.springframework.lang.Contract这使得 NullAway @Contract知道org.springframework.lang包,其中 可用于表达互补语义,以避免代码库中出现不相关的警告。spring-doc.cadn.net.cn

一个很好的例子@Contract声明可以看到Assert.notNull()其中注释为@Contract("null, _ → fail").通过该合约声明,NullAway 将了解 成功调用Assert.notNull().spring-doc.cadn.net.cn

或者,可以设置NullAway:JSpecifyMode=true启用对完整 JSpecify 语义的检查,包括对 数组、vararg 和泛型。请注意,此模式仍在开发中,需要 JDK 22 或更高版本(通常与--releaseJava 编译器标志来配置 预期基线)。建议仅在第二步中启用 JSpecify 模式,在确保代码库 不会使用本节前面提到的推荐配置生成警告。spring-doc.cadn.net.cn

警告抑制

在一些有效的用例中,NullAway 会错误地检测到可空性问题。在这种情况下, 建议禁止显示相关警告并记录原因:spring-doc.cadn.net.cn

  • @SuppressWarnings("NullAway.Init")在字段、构造函数或类级别都可以用来避免不必要的警告 由于字段的延迟初始化——例如,由于实现的类InitializingBean.spring-doc.cadn.net.cn

  • @SuppressWarnings("NullAway") // Dataflow analysis limitation当 NullAway 数据流分析不是 能够检测到涉及可空性问题的路径永远不会发生。spring-doc.cadn.net.cn

  • @SuppressWarnings("NullAway") // Lambda当 NullAway 不考虑执行的断言时可以使用 在 lambda 之外,用于 lambda 内的代码路径。spring-doc.cadn.net.cn

  • @SuppressWarnings("NullAway") // Reflection可用于一些已知返回的反射作 非空值,即使 API 无法表示。spring-doc.cadn.net.cn

  • @SuppressWarnings("NullAway") // Well-known map keys可以在以下情况下使用Map#get调用是使用键执行的 已知存在的值,以及之前插入了非空相关值时。spring-doc.cadn.net.cn

  • @SuppressWarnings("NullAway") // Overridden method does not define nullability可以在超类 不定义可空性(通常当超类来自外部依赖项时)。spring-doc.cadn.net.cn

  • @SuppressWarnings("NullAway") // See github.com/uber/NullAway/issues/1075当 NullAway 无法检测泛型方法中的类型变量空时,可以使用。spring-doc.cadn.net.cn

从 Spring 空安全注解迁移

Spring 空安全注释@Nullable,@NonNull,@NonNullApi@NonNullFieldsorg.springframework.lang包裹是 在 Spring Framework 5 中引入时,JSpecify 还不存在,当时最好的选择是利用 来自 JSR 305(休眠但广泛使用的 JSR)的元注释。从 Spring Framework 7 开始,它们已被弃用,取而代之的是 JSpecify 注释,它提供了显着的增强功能,例如 定义的规范、没有拆分包问题的规范依赖项、更好的工具、更好的 Kotlin 集成、 以及为更多用例更精确地指定可空性的能力。spring-doc.cadn.net.cn

一个关键的区别是,Spring 已弃用的空安全注释(遵循 JSR 305 语义)适用于字段, 参数和返回值;而 JSpecify 注释适用于类型用法。这种细微的区别 在实践中非常重要,因为它允许开发人员区分元素的空性和 数组/varargs 的空性以及定义泛型类型的空性。spring-doc.cadn.net.cn

这意味着必须更新数组和 varargs 空安全声明以保持相同的语义。例如@Nullable Object[] array与 Spring 注释需要更改为Object @Nullable [] array使用 JSpecify 附注。这同样适用于 varargs。spring-doc.cadn.net.cn

还建议将字段和返回值注释移近类型并在同一行上,例如:spring-doc.cadn.net.cn

  • 对于字段,而不是@Nullable private String field使用 Spring 注释,使用private @Nullable String field使用 JSpecify 注释。spring-doc.cadn.net.cn

  • 对于方法返回类型,而不是@Nullable public String method()使用 Spring 注释,使用public @Nullable String method()使用 JSpecify 注释。spring-doc.cadn.net.cn

此外,使用 JSpecify,您无需指定@NonNull重写用@Nullable在超级方法中“撤消”标记为 null 的代码中的可为 null 声明。只需声明它未注释,并且 将应用标记为 null 的默认值(类型用法被视为非 null,除非显式注释为可为 null)。spring-doc.cadn.net.cn