我赌你不清楚Spring中关于Null的这些事

之前一直在某些代码中看到过使用@Nullable 标注过的注释,当时也没有在意到底是什么意思,之后忍不住去调查一番,这篇文章来谈谈Spring中关于Null的那些事。

在Java中不允许你使用类型表示其null的安全性,但Spring Framework 现在在org.sprinngframework.lang包提供以下注释,以便声明API和字段的可空性:

  • @Nullable: 用于指定参数、返回值或者字段可以作为null的注释。
  • @NonNull: 与上述注释相反,表明指定参数、返回值或者字段不允许为null。(不需要@NonNullApi和@NonNullFields适用的参数/返回值和字段)
  • @NonNullApi: 包级别的注释声明非null作为参数和返回值。
  • @NonNullFields:包级别的注释声明字段默认非空

Spring Framework 本身利用了上面这几个注释,但它们也可以运用在任何基于Spring的Java 项目中,以声明空安全api 和 空安全字段。尚未支持泛型和数组元素的可空性,但应该也会在后期版本中支持这俩。

Spring Null-Safety出现在Spring5中,让我们更方便的编写空安全的代码,这叫做null-safety,null-safety不是让我们逃脱不安全的代码,而是在编译时产生警告。 此类警告可以在运行时防止灾难性空指针异常(NPE)。

@NonNull

@NonNull注释是null-safety的所有注释中最重要的一个,我们可以使用此注释在期望对象引用的任何地方声明非空约束:字段、方法参数或者方法返回值。

先来看一个例子

public class Student {
  private String name;
  public String getName() {
    return name;
  }
  public void setName(String name) {
    if(null != null && name.isEmpty()){
      name = null;
    }
    this.name = name;
  }
} 

上述代码对name的校验是有效的,但是存在一个缺陷,如果name被设置为null的话,那么当我们使用name的时候,就会以NullPointerException来结尾。

使用@NonNull

Spring 的null-safety特性能够允许idea或者eclipse报告这个潜在的威胁,例如,如果我们用IDEA对属性加上@NonNull会出现如下的效果。

奇怪,并没有什么变化啊,没看见有潜在的安全提示啊,那是因为你没有在idea进行设置。

设置安全检查

如果你也没有提示的话,可以通过如下的方式设置安全检查:

如果还不好使的话,那就在右侧 configuration annotations 添加一下 @NonNull和 @Nullable 所在的jar包,如下:

添加上,打上 ✅ 即可看到如下效果。

现在fullName 已经被@NonNull 注释添加编译器检查null值的功能了!

测试一下,可以把@NonNull 注释去掉,你的鼠标再放在fullName 上,就没有这句提示了。

@NonNullFields

@NonNull 注解能够帮助你确保null-safety。然而,如果此注释直接装饰所有的字段的话,就会污染整个代码库。

Spring提供了另外一个不允许为null的注解 — @NonNullFields。这个注解适合用在包级别上,通知我们的开发工具注释包中所有的字段,默认的,不允许为null

新建一个Parent类,并在该类所属包下创建一个名为package-info.java的类,创建的不是Java类,而是创建的file,名为package-info.java,如下

package-info.java

@NonNullFields
package com.nullsafety.demo.pojo;
import org.springframework.lang.NonNullFields; 

新建一个Parent.java 类

public class Parent {
  private String son;
  private String age;
  private String name;
  public void setSon(String son) {
    if(son != null && son.isEmpty()){
      son = null;
    }
    this.son = son;
  }
  public void setAge(String age) {
    if(age != null && age.isEmpty()){
      age = null;
    }
    this.age = age;
  }
  public void setName(String name) {
    if(name != null && name.isEmpty()){
      name = null;
    }
    this.name = name;
  }
} 

package-info.java 中的@NonNullFields能够对Parent类中所有的属性起作用,把鼠标放在任意一个属性上,会出现编译期检查的提示

@Nullable

@NonNullFields注释通常比@NonNull更好,因为它有助于减少样板。 但是,有时我们想要从包级别指定的非null约束中免除某些字段,这时候就会使用到@Nullable注解

改造一下Person.java,Person.java 与pack-info.java 处于同一包下

public class Person {
  @NonNull
  private String fullName;
  @Nullable
  private String nickName;
  public String getNickName() {
    return nickName;
  }
  public void setNickName(String nickName) {
    if(nickName != null && nickName.isEmpty()){
      nickName = null;
    }
    this.nickName = nickName;
  }
  public String getFullName() {
    return fullName;
  }
  public void setFullName(String fullName) {
    if(fullName != null && fullName.isEmpty()){
      fullName = null;
    }
    this.fullName = fullName;
  }
} 

在这种情况下,我们使用@Nullable注释来覆盖字段上@NonNullFields的语义。

@NonNullApi

@NonNullFields注释仅适用于其名称所示的字段。 如果我们想对方法的参数和返回值产生相同的影响,我们需要@NonNullApi。

添加 @NonNullApi和 @NonNullFields 在 configure annotations 中,并选用NonNullApi

与@NonNullFields一样,我们需要在package-info.java 中定义@NonNullApi

package-info.java

@NonNullApi
@NonNullFields
package com.nullsafety.demo.pojo;
import org.springframework.lang.NonNullApi;
import org.springframework.lang.NonNullFields; 

加上如下注释后的效果如下: 可以在返回值的时候接受到编译期的提示。

结语

看完文章,你至少应该了解@NonNull, @Nullable, @NonNullFields, @NonNullApi四个注解和各自的作用范围以及如何设置编译期的Null-safety检查。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 详解Spring关于@Resource注入为null解决办法

    初学spring,我在dao层初始化c3p0的时候,使用@Resource注解新建对象是发现注入为null,告诉我 java.lang.NullPointerException. @Repository public class UserDaoImpl implements UserDao { @Resource ComboPooledDataSource dataSource; 反复检查了配置文件,没有发现任何问题,最后在同学的帮助下终于发现了问题的所在,特记录于此,一个教训. 问题就出在我的

  • SpringBoot的拦截器中依赖注入为null的解决方法

    该项目是基于SpringBoot框架的Maven项目. 今天在拦截器中处理拦截逻辑时需要使用注解调用其他方法 并且要从配置文件中读取参数.所以我使用了以下注解: @Reference CoreRedisService redisService; @Value("${channel}") private String channel; @Value("${allowMethod}") private String allowMethod; 一个是获取接口的引用,两外两

  • 详解SpringBoot实现JPA的save方法不更新null属性

    序言:直接调用原生Save方法会导致null属性覆盖到数据库,使用起来十分不方便.本文提供便捷方法解决此问题. 核心思路 如果现在保存某User对象,首先根据主键查询这个User的最新对象,然后将此User对象的非空属性覆盖到最新对象. 核心代码 直接修改通用JpaRepository的实现类,然后在启动类标记此实现类即可. 一.通用CRUD实现类 public class SimpleJpaRepositoryImpl<T, ID> extends SimpleJpaRepository&l

  • 详解Spring Data Jpa当属性为Null也更新的完美解决方案

    开场白 我本来是一名android开发者,突然就对java后端产生了浓烈的兴趣.所以,立马就转到了后端.第一个项目使用的使用Spring Data Jpa来操作数据库的,可是在更新数据的时候发现一个问题,属性值为Null竟然也更新,这就会导致本来没有更新的属性值,全部就成了Null. 原因 经过一番度娘操作,原来Jpa,不知道你是想把属性设置为Null,还是不想. 解决方法 找到一个方法,就是在数据模型上加上注解@DynamicUpdate,可是发现并不好使.而后经过整理,找到以下解决方案 我们

  • 深入浅析Spring 中的Null-Safety

    之前一直在某些代码中看到过使用@Nullable 标注过的注释,当时也没有在意到底是什么意思,所以这篇文章来谈谈Spring中关于Null的那些事. 在Java中不允许让你使用类型表示其null的安全性,但Spring Framework 现在在org.sprinngframework.lang包提供以下注释,以便声明API和字段的可空性: @Nullable: 用于指定参数.返回值或者字段可以作为null的注释. @NonNull: 与上述注释相反,表明指定参数.返回值或者字段不允许为null

  • 深入学习Spring Boot排查 @Transactional 引起的 NullPointerException问题

    写在前面 这个demo来说明怎么排查一个@Transactional引起的NullPointerException. https://github.com/hengyunabc/spring-boot-inside/tree/master/demo-Transactional-NullPointerException 定位 NullPointerException 的代码 Demo是一个简单的spring事务例子,提供了下面一个StudentDao,并用@Transactional来声明事务:

  • 我赌你不清楚Spring中关于Null的这些事

    之前一直在某些代码中看到过使用@Nullable 标注过的注释,当时也没有在意到底是什么意思,之后忍不住去调查一番,这篇文章来谈谈Spring中关于Null的那些事. 在Java中不允许你使用类型表示其null的安全性,但Spring Framework 现在在org.sprinngframework.lang包提供以下注释,以便声明API和字段的可空性: @Nullable: 用于指定参数.返回值或者字段可以作为null的注释. @NonNull: 与上述注释相反,表明指定参数.返回值或者字段

  • 详解Spring 中如何控制2个bean中的初始化顺序

    开发过程中有这样一个场景,2个 bean 初始化逻辑中有依赖关系,需要控制二者的初始化顺序.实现方式可以有多种,本文结合目前对 Spring 的理解,尝试列出几种思路. 场景 假设A,B两个 bean 都需要在初始化的时候从本地磁盘读取文件,其中B加载的文件,依赖A中加载的全局配置文件中配置的路径,所以需要A先于B初始化,此外A中的配置改变后也需要触发B的重新加载逻辑,所以A,B需要注入彼此. 对于下面的模型,问题简化为:我们需要initA()先于initB()得到执行. @Service pu

  • Spring中的事务管理实例详解

    本文实例讲述了Spring中的事务管理.分享给大家供大家参考.具体分析如下: 事务简介: 事务管理是企业级应用程序开发中必不可少的技术,用来确保数据的完整性和一致性 事务就是一系列的动作,它们被当作一个单独的工作单元.这些动作要么全部完成,要么全部不起作用 事务的四个关键属性(ACID) ① 原子性(atomicity):事务室一个原子操作,有一系列动作组成.事务的原子性确保动作要么全部完成,要么完全不起作用 ② 一致性(consistency):一旦所有事务动作完成,事务就被提交.数据和资源就

  • 详析Spring中依赖注入的三种方式

    前言 平常的java开发中,程序员在某个类中需要依赖其它类的方法,则通常是new一个依赖类再调用类实例的方法,这种开发存在的问题是new的类实例不好统一管理,spring提出了依赖注入的思想,即依赖类不由程序员实例化,而是通过spring容器帮我们new指定实例并且将实例注入到需要该对象的类中.依赖注入的另一种说法是"控制反转",通俗的理解是:平常我们new一个实例,这个实例的控制权是我们程序员,而控制反转是指new实例工作不由我们程序员来做而是交给spring容器来做. 在Sprin

  • JSP Spring中Druid连接池配置详解

    JSP Spring中Druid连接池配置 jdbc.properties url=jdbc:postgresql://***.***.***.***:****/**** username=*** password=*** applicationContext.xml中配置bean <!-- 阿里 druid 数据库连接池 --> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSou

  • Spring中bean的继承与抽象代码示例

    我们在应用Spring时,在一般的设计时,肯定要用的抽象类.那在Spring中怎么样配置这些抽象Bean呢.请看下面: 如果两个bean 之间的配置信息非常相似,可利用继承来减少重复配置工作. 继承是指子bean 定义可从父bean 定义继承部分配置信息,也可覆盖特定的配置信息,或者添加一些配置.使用继承配置可以节省很多的配置工作.在实际应用中,通用配置会被配置成模板,可供子bean 继承. 使用abstract 属性 正如前面所介绍的,通用的配置会被配置成模板,而模板不需要实例化,仅仅作为子b

  • spring中的FactoryBean代码示例

    上篇文章中我们介绍了浅谈Spring的两种配置容器,接下来我们就了解下spring中的FactoryBean的相关内容,具体如下. 从SessionFactory说起: 在使用SSH集成开发的时候,我们有时候会在applicationContext.xml中配置Hibernate的信息,下面是配置SessionFactory的一段示例代码: <bean id="sessionFactory" class="org.springframework.orm.hibernat

  • Spring 中使用Quartz实现任务调度

    前言:Spring中使用Quartz 有两种方式,一种是继承特定的基类:org.springframework.scheduling.quartz.QuartzJobBean,另一种则不需要,(推荐使用第二种).下面分别介绍. 1.作业类继承 org.springframework.scheduling.quartz.QuartzJobBean 第一步:定义作业类 java代码 import java.text.SimpleDateFormat; import java.util.Date; i

  • 详解Spring中Bean的加载的方法

    之前写过bean的解析,这篇来讲讲bean的加载,加载要比bean的解析复杂些,从之前的例子开始. Spring中加载一个bean的方式: TestBean bean = factory.getBean("testBean"); 来看看getBean(String name)方法源码, @Override public Object getBean(String name) throws BeansException { return doGetBean(name, null, nul

随机推荐