领先一步
VMware 提供培训和认证,助您加速进步。
了解更多这是 Road to GA 系列中的一篇新博文,这次分享了 Spring 组合中空安全支持的最新状态,作为我之前相关博文 使用 JSpecify 和 NullAway 在 Spring 应用程序中实现空安全 以及相关 Spring I/O 演讲的后续。
是的,我们正在做!这里的“我们”指的是参与 JSpecify 项目的组织、Spring 团队以及您,将升级到 Spring Boot 4 的 Spring 开发者。
话虽如此,我并不认为“十亿美元的错误”是 Tony Hoare 道歉的空引用(null reference)的发明。我认为真正的错误是没有在类型系统中明确表达它,因为正是这种空(nullability)的隐式性导致了生产环境中如此多的 NullPointerException。如果将其明确化,空性就成为了一种零成本抽象,用于表达值可能不存在的情况,并且与现有 API 向后兼容。
JSpecify 旨在提供 一套注解,以及相关的 文档,允许 Java 代码库明确表达其 API 的空性。一个关键点是这些注解不与特定工具绑定,它们被设计为通过详细的 规范 允许多个一致的实现。
随意用 @Nullable 注解一些 API 很容易,但用一个作为构建过程一部分的检查器来完整注解现有代码库,并将空性不一致视为编译错误则更具挑战性。这是 Spring 团队几个月前开始的艰巨任务。
今天,我很高兴地宣布,我们作为一个团队已经实现了我们的目标,使 Spring 组合中的大多数 API 具备空安全,以便授权 Spring 开发者减少或消除生产环境中 NullPointerException 的风险。
更具体地说,以下项目现在提供空安全 API:
一些 Spring 项目尚未提供空安全 API,但计划在近期实现
值得注意的是,Spring 使用的一些依赖项也已使用 JSpecify 注解来标记其 API,并且很可能还会有更多依赖项这样做
Kotlin 2,Spring Framework 7 和 Spring Boot 4 的新基线,会自动将 JSpecify 注解转换为 Kotlin 的空性。再见 平台类型,包括泛型,Spring API 现在看起来就像是原生用 Kotlin 编写的!
受益于 Spring 空安全的最简单方法是升级到 Spring Boot 4 并使用支持 JSpecify 注解的 IDE,以便向开发者提供关于如何处理从 Spring API 使用中检测到的潜在空性问题的反馈。
JetBrains 自 JSpecify 小组成立以来就是其成员,IntelliJ IDEA 团队一直在开发一流的 JSpecify 支持,该支持将在 IntelliJ IDEA 2025.3(预计在几天内发布)中提供。
JetBrains 产品专家 Andrei Kogun 分享了更多细节:
JSpecify 的采用是 Java 生态系统如何通过协作不仅创建了一个新标准,而且还创建了一个开发者在日常工作中真正受益的标准的一个很好的例子。
IntelliJ IDEA 很早就添加了对 JSpecify 的支持——早在规范的初稿出现时就开始了——此后我们一直在改进它。今天,它包括对泛型类型和复杂数据流分析的全面覆盖,使空性检查既强大又直观。社区反馈以及从将其代码库迁移到 JSpecify 的团队的合作,一直是塑造这种支持的关键。
从 IntelliJ IDEA 2025.3 开始,当 classpath 中存在 JSpecify 注解时,IDE 会自动优先选择它们,甚至优先于 JetBrains 自己的注解。这意味着当你修复空性问题时,这些注解不仅会被识别,还会通过快速修复和重构自动生成。
例如,请看下面的截图,IntelliJ IDEA 警告一个潜在的空性问题,并指导开发者如何处理它。

Spring Tools 团队还在致力于 在 Eclipse 和 VSCode 中自动配置 JSpecify,利用 Eclipse 平台中对空性注解的现有支持。
这些 IDE 检查将大大降低运行时 NullPointerException 的风险。
如果你的目标不是减少而是消除应用程序在运行时发生 NullPointerException 的几乎所有可能性,你可以使用 JSpecify 注解来标记你的 Spring Boot 应用程序,我们强烈建议使用像 NullAway 这样的构建时空性检查器。请注意,这比上一节描述的仅仅消费空安全 API 要复杂得多,因此在开始之前请务必完全理解其影响。
Spring 团队一直与加州大学河滨分校计算机科学与工程教授 Manu Sridharan 合作,他也是 NullAway 的负责人。他分享了以下想法:
NullAway 团队很高兴能支持 JSpecify 标准。我们渴望继续与 Spring 团队和其他人合作,以提高整个 Java 生态系统的空安全。
要使您的应用程序空安全,您通常会使用 @NullMarked 注解包,并使用 @Nullable 指定可空类型用法,更多详情请参见 Spring Framework 空安全文档中的相关指南。
要启用构建时检查,您需要在构建中配置 NullAway。请参阅 jspecify-nullaway-demo 以获取 build.gradle、build.gradle.kts 和 pom.xml 的具体示例。
请注意,NullAway JSpecify 模式需要最新版本的 javac,因此如果可以的话,我们建议使用 Java 25,否则大多数 JDK 21.0.8+ 发行版(Oracle JDK 除外)都应该支持 -XDaddTypeAnnotationsToSymbol=true 标志,这将允许 NullAway 正常工作。此标志的 Java 17 后向移植版本可能在未来可用。如果像 Spring 一样,您需要保留 Java 17 基线,您可以使用 Java 25 工具链,并按照 jspecify-nullaway-demo 所示,使用 javac 选项 --release 17 配置您的 Maven 或 Gradle 构建。
请注意,org.springframework.lang 包中以前的 Spring 可空性注解现在已弃用,转而使用 JSpecify 注解。
Spring 团队已努力将此类副作用降至最低,但对于像 Java 25 这样的最新 Java 版本,当导入一个在 classpath 中不存在的类型上带有 JSpecify @Nullable 和 @NonNull 等类型使用注解的类时,javac 可能会报告错误。
目前正在进行讨论和工作,希望能通过恢复 javac 的惰性行为来消除此副作用,使其不再触发此类错误;如果不可能,则将其降级为警告,并提供某种方式来抑制它。有关更多详细信息,请参阅 https://github.com/openjdk/jdk/pull/28018。
JSpecify 工作组正在讨论标准化一种与工具无关的方式来抑制空性检查,例如当它们不相关时。您可以跟踪 https://github.com/jspecify/jspecify/issues/55 相关问题。
NullAway JSpecify 模式仍在发展中,因此请务必查看或报告 相关问题。例如,在使用 lambda 的 Spring 或 Reactor API 时,您可能会遇到 NullAway#1290。目前,您可以使用 @SuppressWarnings("NullAway") 抑制相关警告。
我们还期待对 JDK API 空性进行改进,请参阅 NullAway#950 相关问题。
我喜欢这些空安全改进之处在于,它们促使 Spring 代码库进行了大量改进,从而提高了其质量和健壮性。而且您可以自由决定要利用它们多少。如果您不关心,可以关闭相关警告;在您的 IDE 中修复它们以降低 NullPointerException 的风险;甚至更进一步,使您的应用程序实现空安全。
此外,正如 Spring 工作所示,这在现有项目上是可行的,而不会破坏您的 API,因为您将在概念上用空性信息增强当前类型。
我们希望看到更多的开源库采用 JSpecify,并且我们认为 JSpecify 的采用将有助于 JVM 生态系统和您的应用程序为未来支持 Null-Restricted 和 Nullable Types 做好准备,这可能需要数年才能在实践中实现(作为非预览功能可用,库升级其基线,应用程序也这样做),并带来额外的运行时效率优势。