Spring 中的Null-Safety

以前一直在某些代碼中看到過使用@Nullable 標註過的註釋,當時也沒有在乎究竟是什麼意思,因此這篇文章來談談Spring中關於Null的那些事。java

在Java中不容許讓你使用類型表示其null的安全性,但Spring Framework 如今在org.sprinngframework.lang包提供如下注釋,以便聲明API和字段的可空性:spring

  • @Nullable: 用於指定參數、返回值或者字段能夠做爲null的註釋。
  • @NonNull: 與上述註釋相反,代表指定參數、返回值或者字段不容許爲null。(不須要@NonNullApi和@NonNullFields適用的參數/返回值和字段)
  • @NonNullApi: 包級別的註釋聲明非null做爲參數和返回值。
  • @NonNullFields:包級別的註釋聲明字段默認非空

Spring Framework 自己利用了上面這幾個註釋,但它們也能夠運用在任何基於Spring的Java 項目中,以聲明空安全api 和 空安全字段。還沒有支持泛型和數組元素的可空性,但應也即將發佈在後來的版本。Spring Null-Safety出如今Spring5中,讓咱們更方便的編寫空安全的代碼,這叫作null-safety,null-safety不是讓咱們逃脫不安全的代碼,而是在編譯時產生警告。 此類警告能夠在運行時防止災難性空指針異常(NPE)。api

@NonNull

@NonNull註釋是null-safety的全部註釋中最重要的一個,咱們可使用此註釋在指望對象引用的任何地方聲明非空約束:字段、方法參數或者方法返回值。數組

先來看一個例子安全

public class Student {

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        if(name != null && name.isEmpty()){
            name = null;
        }
        this.name = name;
    }
}

上述代碼對name的校驗是有效的,可是存在一個缺陷,若是name被設置爲null的話,那麼當咱們使用name的時候,就會以NullPointerException來結尾。eclipse

使用@NonNullide

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檢查

歡迎關注

相關文章
相關標籤/搜索