[[null-safety]]
= Null-safety

Although Java does not let you express null-safety with its type system, the Spring Framework codebase is annotated with
https://jspecify.dev/docs/start-here/[JSpecify] annotations to declare the nullness of APIs, fields and related type
usages. Reading the https://jspecify.dev/docs/user-guide/[JSpecify user guide] is highly 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
build time checks and to turn explicit nullness into a way to express the possible absence of value. It is useful in
both Java by leveraging some tooling (https://github.com/uber/NullAway[NullAway] or IDEs supporting null-safety
annotations such as IntelliJ IDEA or Eclipse) and Kotlin where JSpecify annotations are automatically translated to
{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
type usage, a field, a method return type or a parameter. It provides full support for JSpecify annotations,
Kotlin null safety, Java primitive types, as well as a pragmatic check on any `@Nullable` annotation (regardless of the
package).

[[null-safety-libraries]]
== Annotating libraries with JSpecify annotations

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
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
same.

[[null-safety-applications]]
== Leveraging JSpecify annotations in Spring applications

Developing applications with IDEs supporting null-safety annotations, such as IntelliJ IDEA or Eclipse, will provide
warnings in Java and errors in Kotlin when the null-safety contracts are not honored, allowing Spring application
developers to refine their null handling to prevent `NullPointerException` to be thrown at runtime.

Optionally, Spring application developers can annotate their codebase and use https://github.com/uber/NullAway[NullAway]
to enforce null-safety during build time at application level.

[[null-safety-guidelines]]
== Guidelines

The purpose of this section is to share some guidelines proposed for specifying explicitly the nullness of Spring-related
libraries or applications.


[[null-safety-guidelines-jpecify]]
=== JSpecify

The key points to understand is that by default, the nullness of types is unknown in Java, and that non-null type
usages are by far more frequent than nullable ones. In order to keep codebases readable, we typically want to define
that by default, type usages are non-null unless marked as nullable for a specific scope. This is exactly the purpose of
https://jspecify.dev/docs/api/org/jspecify/annotations/NullMarked.html[`@NullMarked`] that is typically set with Spring
at package level via a `package-info.java` file, for example:

[source,java,subs="verbatim,quotes",chomp="-packages",fold="none"]
----
@NullMarked
package org.springframework.core;

import org.jspecify.annotations.NullMarked;
----

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
annotation is specified just before the related type.

For example, for a field:

[source,java,subs="verbatim,quotes"]
----
private @Nullable String fileEncoding;
----

Or for method parameters and return value:

[source,java,subs="verbatim,quotes"]
----
public static @Nullable String buildMessage(@Nullable String message,
                                            @Nullable Throwable cause) {
    // ...
}
----

When overriding a method, nullness annotations are not inherited from the superclass method. That means those
nullness annotations should be repeated if you just want to override the implementation and keep the same API
nullness.

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
https://docs.oracle.com/javase/specs/jls/se17/html/jls-9.html#jls-9.7.4[defined by the Java specification] which may be
initially surprising:

- `@Nullable Object[] array` means individual elements can be null but the array itself can't.
- `Object @Nullable [] array` means individual elements can't be null but the array itself can.
- `@Nullable Object @Nullable [] array` means both individual elements and the array can be null.

The Java specifications also enforces that annotations defined with `@Target(ElementType.TYPE_USE)` like JSpecify
`@Nullable` should be specified after the last `.` with inner or fully qualified types:

 - `Cache.@Nullable ValueWrapper`
 - `jakarta.validation.@Nullable Validator`

https://jspecify.dev/docs/api/org/jspecify/annotations/NonNull.html[`@NonNull`] and
https://jspecify.dev/docs/api/org/jspecify/annotations/NullUnmarked.html[`@NullUnmarked`] should rarely be needed for
typical use cases.

[[null-safety-guidelines-nullaway]]
=== NullAway

==== Configuration

The recommended configuration is:

 - `NullAway:OnlyNullMarked=true` in order to perform nullness checks only for packages annotated with `@NullMarked`.
 - `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
can be used to express complementary semantics to avoid non-relevant null-safety warnings in your codebase.

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
with `@Contract("null, _ -> fail")`. With the configuration above, NullAway will understand that after a successful
invocation, the value passed as a parameter is not null.

Optionally, it is possible to set `NullAway:JSpecifyMode=true` to enable
https://github.com/uber/NullAway/wiki/JSpecify-Support[checks on the full JSpecify semantics], including annotations on
generic types. Be aware that this mode is
https://github.com/uber/NullAway/issues?q=is%3Aissue+is%3Aopen+label%3Ajspecify[still under development] and requires
using JDK 22 or later (typically combined with the `--release` Java compiler flag to configure the
expected baseline). It is recommended to enable the JSpecify mode only as a second step, after making sure the codebase
generates no warning with the recommended configuration mentioned above.

==== Warnings suppression

There are a few valid use cases where NullAway will wrongly detect nullness problems. In such case, it is recommended
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
due to the lazy initialization of fields, for example due to a class implementing
{spring-framework-api}/beans/factory/InitializingBean.html[`InitializingBean`].
 - `@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.
 - `@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.
- `@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.
- `@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.
- `@SuppressWarnings("NullAway") // Overridden method does not define nullness` can be used when the super class does
not define nullness (typically when the super class is coming from a dependency).


[[null-safety-migrating]]
== Migrating from Spring null-safety annotations

Spring null-safety annotations {spring-framework-api}/lang/Nullable.html[`@Nullable`],
{spring-framework-api}/lang/NonNull.html[`@NonNull`],
{spring-framework-api}/lang/NonNullApi.html[`@NonNullApi`], and
{spring-framework-api}/lang/NonNullFields.html[`@NonNullFields`] in the `org.springframework.lang` package have been
introduced in Spring Framework 5 when JSpecify did not exist and the best option was to leverage JSR 305 (a dormant
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
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.

A key difference is that Spring null-safety annotations, following JSR 305 semantics, apply to fields,
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
nullness of arrays/varargs as well as defining the nullness of generic types.

That means array and varargs null-safety declarations have to be updated to keep the same semantic. For example
`@Nullable Object[] array` with Spring annotations needs to be changed to `Object @Nullable [] array` with JSpecify
annotations. Same for varargs.

It is also recommended to move field and return value annotations closer to the type, for example:

 - For fields, instead of `@Nullable private String field` with Spring annotations, use `private @Nullable String field`
with JSpecify annotations.
- For return values, instead of `@Nullable public String method()` with Spring annotations, use
`public @Nullable String method()` with JSpecify annotations.

Also, with JSpecify, you don't need to specify `@NonNull` when overriding a type usage annotated with `@Nullable` in the
super method to "undo" the nullable declaration in null-marked code. Just declare it unannotated and the null-marked
defaults (a type usage is considered non-null unless explicitly annotated as nullable) will apply.

