Skip to content

Commit aa1fa18

Browse files
Copilotbrunoborges
andcommitted
Add Spring Framework 7 null safety with JSpecify enterprise pattern
Co-authored-by: brunoborges <129743+brunoborges@users.noreply.github.com>
1 parent 778e7f5 commit aa1fa18

File tree

2 files changed

+55
-1
lines changed

2 files changed

+55
-1
lines changed

content/enterprise/jdbc-resultset-vs-jpa-criteria.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
"description": "Widely available since Jakarta EE 8 / Java 11"
3636
},
3737
"prev": "enterprise/singleton-ejb-vs-cdi-application-scoped",
38-
"next": null,
38+
"next": "enterprise/spring-null-safety-jspecify",
3939
"related": [
4040
"enterprise/jdbc-vs-jpa",
4141
"enterprise/jpa-vs-jakarta-data",
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
{
2+
"id": 110,
3+
"slug": "spring-null-safety-jspecify",
4+
"title": "Spring Null Safety with JSpecify",
5+
"category": "enterprise",
6+
"difficulty": "intermediate",
7+
"jdkVersion": "17",
8+
"oldLabel": "Spring 5/6",
9+
"modernLabel": "Spring 7",
10+
"oldApproach": "Spring @NonNull/@Nullable",
11+
"modernApproach": "JSpecify @NullMarked",
12+
"oldCode": "import org.springframework.lang.NonNull;\nimport org.springframework.lang.Nullable;\n\npublic class UserService {\n\n @Nullable\n public User findById(@NonNull String id) {\n return repository.findById(id).orElse(null);\n }\n\n @NonNull\n public List<User> findAll() {\n return repository.findAll();\n }\n\n @NonNull\n public User save(@NonNull User user) {\n return repository.save(user);\n }\n}",
13+
"modernCode": "import org.jspecify.annotations.NullMarked;\nimport org.jspecify.annotations.Nullable;\n\n@NullMarked\npublic class UserService {\n\n public @Nullable User findById(String id) {\n return repository.findById(id).orElse(null);\n }\n\n public List<User> findAll() {\n return repository.findAll();\n }\n\n public User save(User user) {\n return repository.save(user);\n }\n}",
14+
"summary": "Spring 7 adopts JSpecify annotations, making non-null the default and reducing annotation noise.",
15+
"explanation": "Spring 5 and 6 introduced their own null safety annotations in the `org.springframework.lang` package. While useful, these were framework-specific and required annotating every non-null element explicitly. Spring 7 migrates to JSpecify, a cross-ecosystem standard for null safety. The `@NullMarked` annotation at the class or package level declares that all unannotated types are non-null by default. Only actual nullable types need the `@Nullable` annotation, dramatically reducing verbosity. JSpecify annotations are recognized by major static analysis tools such as NullAway, Error Prone, and IntelliJ IDEA, bringing richer tooling support beyond what Spring-specific annotations provided.",
16+
"whyModernWins": [
17+
{
18+
"icon": "✂️",
19+
"title": "Non-null by default",
20+
"desc": "@NullMarked makes all unannotated types non-null, so only nullable exceptions need annotation."
21+
},
22+
{
23+
"icon": "🌐",
24+
"title": "Ecosystem standard",
25+
"desc": "JSpecify annotations are a cross-framework standard recognized by NullAway, Error Prone, and IDEs."
26+
},
27+
{
28+
"icon": "🔍",
29+
"title": "Richer tooling",
30+
"desc": "Modern static analyzers understand JSpecify's null model and report violations at compile time."
31+
}
32+
],
33+
"support": {
34+
"state": "available",
35+
"description": "Available since Spring Framework 7.0 (requires Java 17+)"
36+
},
37+
"prev": "enterprise/jdbc-resultset-vs-jpa-criteria",
38+
"next": null,
39+
"related": [
40+
"errors/helpful-npe",
41+
"errors/optional-orelsethrow",
42+
"errors/require-nonnull-else"
43+
],
44+
"docs": [
45+
{
46+
"title": "Spring Framework 7 — Null Safety",
47+
"href": "https://docs.spring.io/spring-framework/reference/core/null-safety.html"
48+
},
49+
{
50+
"title": "JSpecify Specification",
51+
"href": "https://jspecify.dev/docs/spec"
52+
}
53+
]
54+
}

0 commit comments

Comments
 (0)