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