Buchupdate: JSpecify-Null-Annotationen in Spring

 

Ausnahmen vom Typ NullPointerException bleiben bis heute ein lästiges Problem. Glücklicherweise können statische Analysewerkzeuge und moderne IDEs heute zuverlässig prüfen, ob ein Ausdruck dereferenziert wird, der möglicherweise null ist. Problematisch bleiben dabei vor allem Bibliotheken, deren Null-Verhalten nicht präzise beschrieben ist. Aus diesem Grund haben sich Null-Annotationen etabliert, mit denen sich ausdrücken lässt, ob ein Typ null sein darf oder nicht.

 JSpecify

Ab Spring Framework 7 setzt Spring für Null-Safety auf JSpecify (https://jspecify.dev/). JSpecify ist ein gemeinsames Standardisierungsprojekt, das unter anderem von Google, JetBrains (IntelliJ) und dem Spring-Team vorangetrieben wird. Ziel ist eine einheitliche, werkzeugübergreifende Semantik für Null-Sicherheit in Java, im Gegensatz zu älteren Ansätzen wie JSR-305, die nie konsistent umgesetzt wurden.

JSpecify deklariert im Paket org.jspecify.annotations folgende Annotationstypen:

§   @NullMarked: Definiert einen Geltungsbereich (Paket, Klasse, Methode, Modul), in dem alle nicht annotierten Typverwendungen standardmäßig als nicht-null gelten.

§   @Nullable: Markiert eine Typverwendung explizit als null zulässig, also als Wert, der null sein darf. Wird verwendet, um Ausnahmen innerhalb eines @NullMarked-Kontexts zu kennzeichnen.

§   @NonNull: Markiert eine Typverwendung explizit als nicht-null. Vor allem sinnvoll außerhalb eines @NullMarked-Kontexts oder zur bewussten Hervorhebung.

§   @NullUnmarked: Hebt die Wirkung von @NullMarked innerhalb eines Bereichs auf. Nicht annotierte Typverwendungen haben dort wieder keine definierte Null-Semantik. Wird primär für Migration bestehender Codebasen genutzt.

Hinweis

Die Annotationen stammen aus dem Artefakt org.jspecify:jspecify. In Spring Framework 7 ist diese Abhängigkeit in spring-core als API-Abhängigkeit enthalten und in typischen Projekten transitiv verfügbar. Kotlin ab Version 2.1 wertet Annotationen aus org.jspecify.annotations strikt aus.

Prüfung durch Tools

Entwicklungsumgebungen wie IntelliJ IDEA sowie statische Analysewerkzeuge können JSpecify-Annotationen auswerten und auf potenzielle NullPointerException-Risiken hinweisen. Spring Framework verwendet ab Version 7 im Build-Prozess zusätzlich das Analysewerkzeug NullAway (https://github.com/uber/nullaway). NullAway ist ein Compile-Time-Checker auf Basis von Google Error Prone (https://github.com/google/error-prone), der die Einhaltung von Null-Verträgen strikt überprüft. Verstöße – etwa das Zurückgeben von null trotz nicht-null Annotation oder die Übergabe von null an nicht-null Parameter – führen dabei zu Build-Fehlern.

Auf diese Weise stellt Spring sicher, dass die eigene API konsistent annotiert ist und keine versteckten Null-Probleme enthält.

Null-Prüfungen in eigenen Projekten

Dieselbe Art der Prüfung lässt sich auch in eigenen Projekten integrieren, um Null-Fehler frühzeitig und reproduzierbar im Build zu erkennen.

Um @NullMarked auf Paketebene zu aktivieren, legt man eine package-info.java an:

@NullMarked

package com.tutego.date4u.core;

 

import org.jspecify.annotations.NullMarked;

Damit werden in diesem Paket nicht annotierte Typverwendungen standardmäßig als nicht-null interpretiert, sofern nicht explizit @Nullable verwendet wird.

Frühere Spring-Annotationen

Spring stellte über viele Jahre eigene Null-Annotationen im Paket org.springframework.lang[1] bereit. Diese sind seit Spring Framework 7 jedoch veraltet und werden durch JSpecify ersetzt. Die frühere Semantik lässt sich grob wie folgt abbilden:

§   @NonNullApi entspricht funktional einem @NullMarked auf Paketebene

§   @NonNullFields wird ebenfalls durch @NullMarked abgedeckt

§   @Nullable bleibt in gleicher Bedeutung erhalten

§   @NonNull kann weiterhin zur expliziten Kennzeichnung verwendet werden

Dabei handelt es sich jedoch nicht um eine rein mechanische Ersetzung. Während die Spring-Annotationen pauschal auf Parameter, Rückgabewerte und Felder wirkten, basiert JSpecify auf einer präziseren Typsemantik: Annotationen beziehen sich auf konkrete Typverwendungen. Dadurch lassen sich insbesondere generische Typen sowie Array- und Vararg-Elemente präziser beschreiben.

 

Für bestehende Codebasen kann @NullUnmarked verwendet werden, um noch nicht migrierte Bereiche vorübergehend vom @NullMarked-Defaulting auszunehmen und die Migration schrittweise durchzuführen.



[1] https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/lang/package-summary.html

 

Ähnliche Beiträge

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert