Refine null-safety documentation terms
Build and Deploy Snapshot / Build and Deploy Snapshot (push) Waiting to run Details
Build and Deploy Snapshot / Verify (push) Blocked by required conditions Details
Deploy Docs / Dispatch docs deployment (push) Waiting to run Details

Closes gh-34982
This commit is contained in:
Sébastien Deleuze 2025-06-02 23:42:33 +02:00
parent 45ba4ace39
commit 0b14d676cc
1 changed files with 28 additions and 29 deletions

View File

@ -1,15 +1,15 @@
[[null-safety]] [[null-safety]]
= Null-safety = Null-safety
Although Java does not let you express null-safety with its type system, the Spring Framework codebase is annotated with Although Java does not let you express nullness markers with its type system yet, the Spring Framework codebase is
https://jspecify.dev/docs/start-here/[JSpecify] annotations to declare the nullness of APIs, fields and related type annotated with https://jspecify.dev/docs/start-here/[JSpecify] annotations to declare the nullability of its APIs,
usages. Reading the https://jspecify.dev/docs/user-guide/[JSpecify user guide] is highly recommended in order to get fields and related type usages. Reading the https://jspecify.dev/docs/user-guide/[JSpecify user guide] is highly
familiar with those annotations and semantics. recommended in order to get familiar with those annotations and semantics.
The primary goal of this explicit null-safety arrangement is to prevent `NullPointerException` to be thrown at runtime via The primary goal of this null-safety arrangement is to prevent `NullPointerException` to be thrown at runtime via build
build time checks and to turn explicit nullness into a way to express the possible absence of value. It is useful in time checks and to turn explicit nullability into a way to express the possible absence of value. It is useful in both
both Java by leveraging some tooling (https://github.com/uber/NullAway[NullAway] or IDEs supporting null-safety Java by leveraging some tooling (https://github.com/uber/NullAway[NullAway] or IDEs supporting JSpecify annotations
annotations such as IntelliJ IDEA or Eclipse) and Kotlin where JSpecify annotations are automatically translated to such as IntelliJ IDEA for example) and Kotlin where JSpecify annotations are automatically translated to
{kotlin-docs}/null-safety.html[Kotlin's null safety]. {kotlin-docs}/null-safety.html[Kotlin's null safety].
The {spring-framework-api}/core/Nullness.html[`Nullness` Spring API] can be used at runtime to detect the nullness of a The {spring-framework-api}/core/Nullness.html[`Nullness` Spring API] can be used at runtime to detect the nullness of a
@ -21,7 +21,7 @@ package).
== Annotating libraries with JSpecify annotations == Annotating libraries with JSpecify annotations
As of Spring Framework 7, the Spring Framework codebase leverages JSpecify annotations to expose null-safe APIs and As of Spring Framework 7, the Spring Framework codebase leverages JSpecify annotations to expose null-safe APIs and
to check the consistency of those null-safety declarations with https://github.com/uber/NullAway[NullAway] as part of to check the consistency of those nullability declarations with https://github.com/uber/NullAway[NullAway] as part of
its build. It is recommended for each library depending on Spring Framework (Spring portfolio projects), as its build. It is recommended for each library depending on Spring Framework (Spring portfolio projects), as
well as other libraries related to the Spring ecosystem (Reactor, Micrometer and Spring community projects), to do the well as other libraries related to the Spring ecosystem (Reactor, Micrometer and Spring community projects), to do the
same. same.
@ -29,18 +29,18 @@ same.
[[null-safety-applications]] [[null-safety-applications]]
== Leveraging JSpecify annotations in Spring applications == Leveraging JSpecify annotations in Spring applications
Developing applications with IDEs supporting null-safety annotations, such as IntelliJ IDEA or Eclipse, will provide Developing applications with IDEs supporting nullness annotations will provide warnings in Java and errors in Kotlin
warnings in Java and errors in Kotlin when the null-safety contracts are not honored, allowing Spring application when the nullability contracts are not honored, allowing Spring application developers to refine their null handling to
developers to refine their null handling to prevent `NullPointerException` to be thrown at runtime. prevent `NullPointerException` to be thrown at runtime.
Optionally, Spring application developers can annotate their codebase and use https://github.com/uber/NullAway[NullAway] Optionally, Spring application developers can annotate their codebase and use build plugins like
to enforce null-safety during build time at application level. https://github.com/uber/NullAway[NullAway] to enforce null-safety during build time at application level.
[[null-safety-guidelines]] [[null-safety-guidelines]]
== Guidelines == Guidelines
The purpose of this section is to share some guidelines proposed for specifying explicitly the nullness of Spring-related The purpose of this section is to share some guidelines proposed for specifying explicitly the nullability of
libraries or applications. Spring-related libraries or applications.
[[null-safety-guidelines-jspecify]] [[null-safety-guidelines-jspecify]]
@ -62,7 +62,7 @@ import org.jspecify.annotations.NullMarked;
In the various Java files belonging to the package, nullable type usages are defined explicitly with In the various Java files belonging to the package, nullable type usages are defined explicitly with
https://jspecify.dev/docs/api/org/jspecify/annotations/Nullable.html[`@Nullable`]. It is recommended that this https://jspecify.dev/docs/api/org/jspecify/annotations/Nullable.html[`@Nullable`]. It is recommended that this
annotation is specified just before the related type. annotation is specified just before the related type on the same line.
For example, for a field: For example, for a field:
@ -81,9 +81,8 @@ public static @Nullable String buildMessage(@Nullable String message,
} }
---- ----
When overriding a method, nullness annotations are not inherited from the superclass method. That means those When overriding a method, JSpecify annotations are not inherited from the superclass method. That means they should be
nullness annotations should be repeated if you just want to override the implementation and keep the same API repeated if you just want to override the implementation and keep the same nullability.
nullness.
With arrays and varargs, you need to be able to differentiate the nullness of the elements from the nullness of With arrays and varargs, you need to be able to differentiate the nullness of the elements from the nullness of
the array itself. Pay attention to the syntax the array itself. Pay attention to the syntax
@ -111,10 +110,10 @@ typical use cases.
The recommended configuration is: The recommended configuration is:
- `NullAway:OnlyNullMarked=true` in order to perform nullness checks only for packages annotated with `@NullMarked`. - `NullAway:OnlyNullMarked=true` in order to perform nullability checks only for packages annotated with `@NullMarked`.
- `NullAway:CustomContractAnnotations=org.springframework.lang.Contract` which makes NullAway aware of the - `NullAway:CustomContractAnnotations=org.springframework.lang.Contract` which makes NullAway aware of the
{spring-framework-api}/lang/Contract.html[@Contract] annotation in the `org.springframework.lang` package which {spring-framework-api}/lang/Contract.html[@Contract] annotation in the `org.springframework.lang` package which
can be used to express complementary semantics to avoid non-relevant null-safety warnings in your codebase. can be used to express complementary semantics to avoid non-relevant warnings in your codebase.
A good example of `@Contract` benefits is A good example of `@Contract` benefits is
{spring-framework-api}/util/Assert.html#notNull(java.lang.Object,java.lang.String)[`Assert#notnull`] which is annotated {spring-framework-api}/util/Assert.html#notNull(java.lang.Object,java.lang.String)[`Assert#notnull`] which is annotated
@ -131,22 +130,22 @@ generates no warning with the recommended configuration mentioned above.
==== Warnings suppression ==== Warnings suppression
There are a few valid use cases where NullAway will wrongly detect nullness problems. In such case, it is recommended There are a few valid use cases where NullAway will wrongly detect nullability problems. In such case, it is recommended
to suppress related warnings and to document the reason: to suppress related warnings and to document the reason:
- `@SuppressWarnings("NullAway.Init")` at field, constructor or class level can be used to avoid unnecessary warnings - `@SuppressWarnings("NullAway.Init")` at field, constructor or class level can be used to avoid unnecessary warnings
due to the lazy initialization of fields, for example due to a class implementing due to the lazy initialization of fields, for example due to a class implementing
{spring-framework-api}/beans/factory/InitializingBean.html[`InitializingBean`]. {spring-framework-api}/beans/factory/InitializingBean.html[`InitializingBean`].
- `@SuppressWarnings("NullAway") // Dataflow analysis limitation` can be used when NullAway dataflow analysis is not - `@SuppressWarnings("NullAway") // Dataflow analysis limitation` can be used when NullAway dataflow analysis is not
able to detect that the path involving a nullness problem will never happen. able to detect that the path involving a nullability problem will never happen.
- `@SuppressWarnings("NullAway") // Lambda` can be used when NullAway does not take into account assertions performed - `@SuppressWarnings("NullAway") // Lambda` can be used when NullAway does not take into account assertions performed
outside of a lambda for the code path within the lambda. outside of a lambda for the code path within the lambda.
- `@SuppressWarnings("NullAway") // Reflection` can be used for some reflection operations that are known returning - `@SuppressWarnings("NullAway") // Reflection` can be used for some reflection operations that are known returning
non-null values even if that can't be expressed by the API. non-null values even if that can't be expressed by the API.
- `@SuppressWarnings("NullAway") // Well-known map keys` can be used when `Map#get` invocations are done with keys known - `@SuppressWarnings("NullAway") // Well-known map keys` can be used when `Map#get` invocations are done with keys known
to be present and non-null related values inserted previously. to be present and non-null related values inserted previously.
- `@SuppressWarnings("NullAway") // Overridden method does not define nullness` can be used when the super class does - `@SuppressWarnings("NullAway") // Overridden method does not define nullability` can be used when the super class does
not define nullness (typically when the super class is coming from a dependency). not define nullability (typically when the super class is coming from a dependency).
[[null-safety-migrating]] [[null-safety-migrating]]
@ -160,9 +159,9 @@ introduced in Spring Framework 5 when JSpecify did not exist and the best option
but widespread JSR) meta-annotations. They are deprecated as of Spring Framework 7 in favor of but widespread JSR) meta-annotations. They are deprecated as of Spring Framework 7 in favor of
https://jspecify.dev/docs/start-here/[JSpecify] annotations, which provide significant enhancements such as properly https://jspecify.dev/docs/start-here/[JSpecify] annotations, which provide significant enhancements such as properly
defined specifications, a canonical dependency with no split-package issue, better tooling, better Kotlin integration defined specifications, a canonical dependency with no split-package issue, better tooling, better Kotlin integration
and the capability to specify the nullness more precisely for more use cases. and the capability to specify the nullability more precisely for more use cases.
A key difference is that Spring null-safety annotations, following JSR 305 semantics, apply to fields, A key difference is that Spring deprecated null-safety annotations, following JSR 305 semantics, apply to fields,
parameters and return values while JSpecify annotations apply to type usages. This subtle difference parameters and return values while JSpecify annotations apply to type usages. This subtle difference
is in practice pretty significant, as it allows for example to differentiate the nullness of elements from the is in practice pretty significant, as it allows for example to differentiate the nullness of elements from the
nullness of arrays/varargs as well as defining the nullness of generic types. nullness of arrays/varargs as well as defining the nullness of generic types.
@ -171,7 +170,7 @@ That means array and varargs null-safety declarations have to be updated to keep
`@Nullable Object[] array` with Spring annotations needs to be changed to `Object @Nullable [] array` with JSpecify `@Nullable Object[] array` with Spring annotations needs to be changed to `Object @Nullable [] array` with JSpecify
annotations. Same for varargs. annotations. Same for varargs.
It is also recommended to move field and return value annotations closer to the type, for example: It is also recommended to move field and return value annotations closer to the type on the same line, for example:
- For fields, instead of `@Nullable private String field` with Spring annotations, use `private @Nullable String field` - For fields, instead of `@Nullable private String field` with Spring annotations, use `private @Nullable String field`
with JSpecify annotations. with JSpecify annotations.