Skip to content

Commit b3be9e2

Browse files
author
Jonas Keßler
committed
more info on null-safety
1 parent b00c792 commit b3be9e2

File tree

5 files changed

+98
-14
lines changed

5 files changed

+98
-14
lines changed

readme.md

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,25 @@ This project covers several changes in spring-boot 2.0.0 which are worth knowing
44

55
Currently this project contains changes from the latest released milestone 2.0.0M3.
66

7-
__Code structure__
7+
__Code structure__<br/>
88
This repository contains two spring-boot-projects:
99
- `spring-boot-2-demo` which shows the new features, see below.
1010
- `spring-boot-1-demo` which is meant to show the old behaviour
1111

1212
# Features inherited from Spring Framework 5.0.0
1313

14-
## @Nullable
15-
See package `de.acando.jk.bootdemo.newfeatures.nullable`.
14+
## @Nullable and @NonNullApi
1615

17-
`@Nullable` annotation can be used in controller to define that e.g. a RequestParam is not required.
16+
> Intention of the spring developers was to "leverage [JSR 305](https://jcp.org/en/jsr/detail?id=305) (...) meta-annotations to specify explicitly
17+
null-safety of Spring Framework parameters and return values", compare this [commit message](https://github.com/spring-projects/spring-framework/commit/87598f48e41d483745aba56cbf4e998c6f6d680c#diff-31f527c92f7d3887b2320e4a28e7be8a). <br/>
18+
> Another aim was to make the Spring-Framework null-safe for Kotlin. The annotations are also used for static code analysis, e.g. IntelliJ produces warnings when unsafe nullable usages are detected.
19+
20+
Besides these points described above, the `@Nullable` annotation can be used to declare that a object can be null which means it is not required. Places where it can be used:
21+
- On field injection
22+
- On controller injection
23+
- In controllers requestmapping methods to define that e.g. a RequestParam is not required.
24+
25+
Example implementation see package `newfeaturesin2.nullable` in both projects.
1826

1927
## Candidate Component Index
2028
There is a new package `spring-context-indexer` which, if included as dependency, creates a index file `META-INF/spring.components` at build time. This file lists all candidate components (Spring Beans) as well as JPA candidates (Entities, Repositories).
@@ -26,13 +34,16 @@ Add following maven dependendy to active the new feature:
2634
<artifactId>spring-context-indexer</artifactId>
2735
</dependency>
2836

37+
For more details for this new feature see the [commit message](https://github.com/snicoll/spring-framework/commit/dc160f6fd1f3623bf14b375c14a9b5065e660377) and the [JIRA Discussion](https://jira.spring.io/browse/SPR-11890).
38+
2939
Simple hint for taking a look at the new mechanism via debugging in an IDE:
3040
- CAUTION: This is just for taking a look at the new mechanism, should be reverted if going back to developing!
3141
- Run a build, then copy `META-INF/spring.components` from the jar file to `src/main/resources/META-INF/spring.components`
3242
- Breakpoints in
3343
* `CandidateComponentsIndexLoader.loadIndex(..)` where the index file is loaded
3444
* `ClassPathScanningCandidateComponentProvider.findCandidateComponents(..)` where the index is used ix exists, otherwise class path is scanned
3545

46+
To see that it works for JPA related candidates, checkout the branch `jpa` and run a build.
3647

3748

3849
## Changed or removed properties
@@ -50,7 +61,10 @@ Simple hint for taking a look at the new mechanism via debugging in an IDE:
5061
Can be changed with property `management.context-path`.
5162

5263
## @ConfigurationProperties needs @Validated
53-
`@Valiadated` is now required, if there are any validation annotations in a `@ConfigurationProperties` class, which should be requested at startup.
64+
65+
`@Validated` is now required, if there are any validation annotations in a `@ConfigurationProperties` class, which should be requested at startup.
66+
67+
Example implementation: see package `newfeaturesin2.properties` in both projects. In v1.x the application fails starting, if the property `test.any` is not set. In v2.x it only fails if the `@Validated` annotation is used.
5468

5569
## New spring-boot-starter-json
5670
Includes dependencies `jackson-databind`, `jackson-datatype-jsr310` (necessary for convenient handling of java 8 data types) among others. The `spring-boot-starter-json` is in 2.0.0 included everywhere, where `jackson-databind` was used in 1.x. E.g. in the `spring-boot-starter-web`, so for my normal project setup there is no jackson dependency to be added explicitly anymore.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package de.acando.jk.boot1demo.newfeaturesin2.nullable;
2+
3+
/**
4+
*
5+
* @author Jonas Keßler (jonas.kessler@acando.de)
6+
*/
7+
public interface ServiceWithoutImplementation {
8+
9+
public default String tellMeSomething() {
10+
return "Spring is awesome";
11+
}
12+
}

spring-boot-1-demo/src/main/java/de/acando/jk/boot1demo/newfeaturesin2/nullable/TestController.java

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import java.util.Optional;
44

5+
import org.springframework.beans.factory.annotation.Autowired;
56
import org.springframework.web.bind.annotation.GetMapping;
67
import org.springframework.web.bind.annotation.RequestMapping;
78
import org.springframework.web.bind.annotation.RequestParam;
@@ -16,21 +17,40 @@
1617
public class TestController {
1718

1819
/*
19-
* Nullable geht noch nicht in Spring < 5
20+
* Alternative solutions for field injection without @Nullable annotation:
21+
* - @Autowired(required = false) ServiceWithoutImplementation
22+
* - @Autowired Optional<ServiceWithoutImplementation>
2023
*/
21-
// @GetMapping()
22-
// public String hello(@Nullable @RequestParam String name) {
23-
// return "Hello " + name;
24+
25+
// @Autowired(required = false)
26+
// private ServiceWithoutImplementation service;
27+
28+
@Autowired
29+
private Optional<ServiceWithoutImplementation> service;
30+
31+
32+
/*
33+
* Alternative solution for constructor injection without @Nullable annotation
34+
*/
35+
// private final Optional<ServiceWithoutImplementation> service;
36+
// public TestController(Optional<ServiceWithoutImplementation> service) {
37+
// this.service = service;
2438
// }
2539

40+
41+
2642
/*
27-
* Alternative solutions:
28-
* - @RequestParam(required = false)
29-
* - Optional<String>
43+
* Nullable annotation not available Spring < 5
44+
* Alternative solutions here:
45+
* - @RequestParam(required = false) String
46+
* - @RequestParam Optional<String>
3047
*/
3148
@GetMapping
3249
public String hello(@RequestParam Optional<String> name) {
33-
return "Hello " + name.orElseGet(() -> "unknown");
50+
return "Hello " + name.orElse("unknown")
51+
+ " - "
52+
+ service.orElse(new ServiceWithoutImplementation() {
53+
}).tellMeSomething();
3454
}
3555

3656

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package de.acando.jk.bootdemo.newfeaturesin2.nullable;
2+
3+
/**
4+
*
5+
* @author Jonas Keßler (jonas.kessler@acando.de)
6+
*/
7+
public interface ServiceWithoutImplementation {
8+
9+
public default String tellMeSomething() {
10+
return "Spring is awesome";
11+
}
12+
}

spring-boot-2-demo/src/main/java/de/acando/jk/bootdemo/newfeaturesin2/nullable/TestController.java

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package de.acando.jk.bootdemo.newfeaturesin2.nullable;
22

3+
import java.util.Optional;
4+
35
import org.springframework.lang.Nullable;
46
import org.springframework.web.bind.annotation.GetMapping;
57
import org.springframework.web.bind.annotation.RequestMapping;
@@ -10,9 +12,33 @@
1012
@RequestMapping("/test")
1113
public class TestController {
1214

15+
/*
16+
* @Nullable usage at field injection
17+
*/
18+
// @Nullable
19+
// @Autowired
20+
// private ServiceWithoutImplementation service;
21+
22+
23+
/*
24+
* @Nullable usage in constructor injection
25+
*
26+
* FYI, since spring 4.3:
27+
* -> It is no longer necessary to specify the @Autowired annotation if the target bean only defines one constructor.
28+
* compare https://docs.spring.io/spring/docs/current/spring-framework-reference/html/new-in-4.3.html
29+
*/
30+
private final ServiceWithoutImplementation service;
31+
public TestController(@Nullable ServiceWithoutImplementation service) {
32+
this.service = service;
33+
}
34+
1335
@GetMapping()
1436
public String hello(@Nullable @RequestParam String name) {
15-
return "Hello " + name;
37+
return "Hello " + Optional.ofNullable(name).orElse("unknown")
38+
+ " - "
39+
+ Optional.ofNullable(service)
40+
.orElse(new ServiceWithoutImplementation() {
41+
}).tellMeSomething();
1642
}
1743

1844
}

0 commit comments

Comments
 (0)