一、背景与知识点
1. 背景:
空指针异常(NullPointerException,简称 NPE)曾被称作 “十亿美元的错误”。但 Spring 团队认为,真正的错误并非 “null” 的存在,而是 Java 语言没有显式表达可空性(nullability) 。
Spring 团队正式宣布:Spring Boot 4 与整个 Spring 全家桶已全面实现 Null 安全(Null-Safety) ,通过引入 JSpecify 标准注解,让 Java 开发者终于有能力从框架层面杜绝 NullPointerException。
它的核心理念是:显式表达每一个可能为空的类型 。
2. 四个 org.jspecify.annotations 下的注解:
NullMarked
NullUnmarked
Nullable
NonNull
3.安装插件:
io.spring.nullability
4. 最佳实践
- 始终使用
@NullMarked注解:这可以帮助你在整个项目中强制实施可空性检查。在类级别启用 Null 安全模式,默认所有类型不可为 null; - 合理使用
@Nullable和@NonNull注解:明确标记哪些变量和方法返回值是可空的,哪些是不可空的。 - 用插件直接在编译期完成检查:确保你的代码始终符合
JSpecify的规范。
二、实操(采用 gradle 项目 ):
因为 gradle 配置比 maven 简洁的多,另外配置语法采用了 kotlin ,语法结构上更加规范,并有相应错误检查。
-
步骤1:创建
SpringBoot4.X项目(略) -
步骤2:安装检验
nullability的插件,spring 团队开发的插件id("io.spring.nullability") version "0.0.12"完整的
build.gradle.kts配置 :plugins { java id("org.springframework.boot") version "4.0.5" id("io.spring.dependency-management") version "1.1.7" id("io.spring.nullability") version "0.0.12" // 检验 `nullability` 的插件 } group = "com.yiyi" version = "0.0.1-SNAPSHOT" description = "web" java { toolchain { languageVersion = JavaLanguageVersion.of(25) } } repositories { mavenCentral() maven { name = "Central Portal Snapshots" url = uri("https://central.sonatype.com/repository/maven-snapshots/") } } dependencies { implementation(platform("org.springframework.ai:spring-ai-bom:2.0.0-M4")) implementation("org.springframework.boot:spring-boot-starter-webflux") implementation("org.springframework.boot:spring-boot-starter-data-r2dbc") implementation("io.asyncer:r2dbc-mysql:1.4.1") // AI implementation("org.springframework.ai:spring-ai-starter-model-zhipuai") implementation("org.springframework.ai:spring-ai-starter-model-deepseek") annotationProcessor("org.projectlombok:lombok") compileOnly("org.projectlombok:lombok") testImplementation("org.springframework.boot:spring-boot-starter-webflux-test") testRuntimeOnly("org.junit.platform:junit-platform-launcher") } tasks.withType<Test> { useJUnitPlatform() } -
步骤3:在 JAVA 代码的举例
我们以 web 项目中通用相应体
R<T>作为例子讲解。要求:code,message不能为空 ,data允许为空(必须声明)。因为类上配置了NullMarked,则默认的值都为非空。完整的
R.java代码package com.yiyi.web.model; import lombok.Data; import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; /** * 通用响应体(适配前后端统一的 {code, msg, data} 格式) * @param <T> 数据泛型 */ @NullMarked @Data public class R<T> { /** * 状态码:200成功,其他为失败 */ private int code; /** * 提示信息 */ private String msg = ""; /** * 响应数据 */ @Nullable private T data; // 成功响应(带数据) public static <T> R<T> success(@Nullable T data) { R<T> response = new R<>(); response.setCode(200); response.setMsg("操作成功"); response.setData(data); return response; } // 成功响应(无数据) public static <T> R<T> success() { return success(null); } // 失败响应 public static <T> R<T> error(int code, String msg) { R<T> response = new R<>(); response.setCode(code); response.setMsg(msg); response.setData(null); return response; } // 通用失败响应(默认500) public static <T> R<T> error(String msg) { return error(500, msg); } } -
步骤4: 重要说明
- 安装了
io.spring.nullability插件后,你项目中的类必须加上注解@NullMarked或@NullUnmarked - 为了更好的避免 null 值,先在类上注解
@NullMarked, 若某个属性类允许很多的null值,则可在此类上注解@NullUnmarked R.java类中的message 必须赋予初始值(要求非空)- 在方法里面,允许为空的要注解上
@Nullable,默认都是非空的 - 集合元素使用List<@Nullable T>标记元素可能为空
- 安装了
三、结语
编译器变成安检员,在代码运行前就拦截所有"携带违禁品(null)"的行为。让你再也不用为了在运行时或上线时抛出 NullException 而烦恼。而对哪些允许为 null值的变量也标记了明确的注解(至少你应该明确了为什么允许null)。
项目中为了避免编译器报错,而大量的使用非检查null注解 @NullUnmarked,则其提供的好处就不再存在,那甚至去掉此功能更好(至少代码量少一点,编译的开销也少一些)
注解体系不是要彻底消灭null值——null本身是有用的,表示"值不存在"这一合理业务场景。它真正解决的是"意外的null",将"可能为空"从隐性知识变为显性契约,从依赖开发者的细心变为依赖工具的保障。
转载自CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/Yiyi_Coding/article/details/159864248



