关注

SpringBoot4.X: 彻底消灭 NullPointerException

一、背景与知识点

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. 最佳实践
  1. 始终使用 @NullMarked 注解:这可以帮助你在整个项目中强制实施可空性检查。在类级别启用 Null 安全模式,默认所有类型不可为 null;
  2. 合理使用 @Nullable@NonNull 注解:明确标记哪些变量和方法返回值是可空的,哪些是不可空的。
  3. 用插件直接在编译期完成检查:确保你的代码始终符合 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

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--