Slide 1

Slide 1 text

Spring 5 Themes & Trends Stéphane Nicoll - @snicoll

Slide 2

Slide 2 text

Last 4.x feature release: 4.3 2 • Released on June 2016 • Extended support life until 2019 • on JDK 6, 7, 8 • on Tomcat 6, 7, 8.0, 8.5 • on WebSphere 7, 8.0, 8.5 and 9 • Programming model refinements brought forward to JDK 6+ • DI & MVC refinements • Composed annotations

Slide 3

Slide 3 text

A new framework generation for 2017+ 3 • 5.0 GA available • Spring Boot 2.0 M5 next week! • Major baseline upgrade • JDK 8+ and JavaEE 7 (Servlet 3.1+, Bean Validation 1.1, JMS 2.0+, JPA 2.1+) • Support of JUnit 5 (next to JUnit 4.12) • Comprehensive integration with JavaEE 8 API level • Servlet 4.0, Bean Validation 2.0, JPA 2.2, JSON Binding API 1.0 • e.g. Tomcat 9.0, Hibernate Validator 6.0, Apache Johnzon 1.1

Slide 4

Slide 4 text

@snicoll 4 JDK 9

Slide 5

Slide 5 text

@snicoll 5 HTTP/2

Slide 6

Slide 6 text

@snicoll 6 Reactive Spring

Slide 7

Slide 7 text

7 ∞

Slide 8

Slide 8 text

8 Going Reactive: More for scalability and stability than for speed

Slide 9

Slide 9 text

Blocking + Thread pools 9 HTTP request HTTP response ⏳ ⚙ ⏳ ✍ ⏳ Thread

Slide 10

Slide 10 text

Non-blocking and event-loop 10 IO Selector Thread Worker Threads ⚙ ⚙ ✍ ✍ ⚙ ⚙ ✍ ✍ ⚙ ⚙ ✍

Slide 11

Slide 11 text

11 From blocking to event-based Neutral to latency

Slide 12

Slide 12 text

Imperative style 12 public interface PersonRepository {
 
 Person findById(String id);
 
 List findAll();
 
 void save(Person person);
 
 } try {
 Person p = personRepository.findById(id);
 // ...
 handle(p);
 }
 catch (IOException ex) {
 // ...
 } Blocking

Slide 13

Slide 13 text

Functional style 13 personRepository.findById(id)
 .map(this::handle)
 .subscribe(); public interface PersonRepository {
 
 Mono findById(String id);
 
 Flux findAll();
 
 Mono save(Person person);
 
 } Neutral to latency (event based)

Slide 14

Slide 14 text

Reactive Streams 14 Publisher Subscriber subscribe request(n) Backpressure Error|Complete onNext(data) onNext(data) onNext(data)

Slide 15

Slide 15 text

Reactive Streams API 15 public interface Publisher { void subscribe(Subscriber super T> s); } public interface Subscriber { void onSubscribe(Subscription s); void onNext(T t); void onError(Throwable t); void onComplete(); } public interface Subscription { void request(long n); void cancel(); } public interface Processor extends Subscriber, Publisher { }

Slide 16

Slide 16 text

Functional style 16 personRepository.findById(id)
 .map(this::handle)
 .subscribe(); public interface PersonRepository {
 
 Mono findById(String id);
 
 findAll();
 
 Mono save(Person person);
 
 } Neutral to latency (event based) Flux

Slide 17

Slide 17 text

17 is a Publisher for 0..n elements Flux

Slide 18

Slide 18 text

18 Mono is a Publisher for 0..1 element

Slide 19

Slide 19 text

@snicoll Demo

Slide 20

Slide 20 text

20

Slide 21

Slide 21 text

Reactive Web Controller 21 @GetMapping("/users/{id}")
 Mono getById(@PathVariable String id) {
 return this.userRepository.findOne(id);
 }
 
 @GetMapping("/users")
 Flux all() {
 return this.userRepository.findAll();
 }
 
 @PostMapping("/users")
 Mono save(@RequestBody Mono user) {
 return this.userRepository.save(user).then();
 }

Slide 22

Slide 22 text

New Web Client API 22 WebClient client = WebClient.create(); Mono githubUser = client .get() .uri("https://api.github.com/users/{username}", username) .retrieve() .bodyToMono(GithubUser.class); Mono twitterUser = client .get() .uri("https://api.twitter.com/1.1/users/show.json?screen_name={username}", username) .retrieve() .bodyToMono(TwitterUser.class); return githubUser.and(twitterUser, (github, twitter)-> new AppUser(github, twitter));

Slide 23

Slide 23 text

23 @Controller, @RequestMapping Spring MVC Servlet API Servlet Container Spring WebFlux.fn Servlet 3.1, Netty, Undertow Spring WebFlux HTTP / Reactive Streams

Slide 24

Slide 24 text

@snicoll 24 Functional APIs

Slide 25

Slide 25 text

Functional bean registration API (work in progress) 25 • Alternative mechanism to configure the ApplicationContext • Full control over beans registration public static void main(String[] args) { GenericApplicationContext ctx = new GenericApplicationContext(); ctx.registerBean(A.class); ctx.registerBean(B.class, () -> new B(ctx.getBean(A.class))); ctx.refresh(); }

Slide 26

Slide 26 text

Functional-style web routing 26 RouterFunction> route =
 route(GET("/users"), )
 .and(route(GET("/users/{id}"), )
 .and(route(POST("/users"), ))); request -> {
 Flux people = repository.findAll();
 return ServerResponse.ok().body(people, User.class);
 } request -> {
 String personId = request.pathVariable("id");
 Mono person = repository.findById(personId);
 return ServerResponse.ok().body(person, User.class);
 } request -> {
 Mono user = request.bodyToMono(User.class);
 return ServerResponse.ok().build(repository.saveAll(user).then());
 }

Slide 27

Slide 27 text

Functional-style web routing 27 RouterFunction> route =
 route(GET(“/users"), )
 .and(route(GET(“/users/{id}”), )
 .and(route(POST(“/users”), ))); this::findAll this::findById this::save

Slide 28

Slide 28 text

@snicoll 28 Kotlin Support

Slide 29

Slide 29 text

Type inference 29 @RestController class UserController(val repo: UserRepository) { @GetMapping("/user/{id}") fun findOne(@PathVariable id: String): Mono { } @GetMapping("/user") fun findAll() : Flux { } } return repo.findOne(id) return repo.findAll()

Slide 30

Slide 30 text

Type inference 30 @RestController class UserController(val repo: UserRepository) { @GetMapping("/user/{id}") fun findOne(@PathVariable id: String) = @GetMapping("/user") fun findAll() : Flux = } repo.findOne(id) repo.findAll()

Slide 31

Slide 31 text

Extensions 31 fun findAll(request: ServerRequest): Mono { } inline fun ServerResponse.BodyBuilder .body(publisher: Publisher) = body(publisher, T::class.java) ServerResponseExtensions provided by spring-webflux return ok().body(repo.findAll(), User::class.java)

Slide 32

Slide 32 text

Extensions 32 fun findAll(request: ServerRequest) = inline fun ServerResponse.BodyBuilder .body(publisher: Publisher) = body(publisher, T::class.java) ServerResponseExtensions provided by spring-webflux ok().body(repo.findAll())

Slide 33

Slide 33 text

Leverage Kotlin nullable information 33 @GetMapping("/foo") fun foo(@RequestParam( ) bar: String?) = … http://localhost:8080/foo required = false

Slide 34

Slide 34 text

Leverage Kotlin nullable information 34 @GetMapping("/foo") fun foo(@RequestParam bar: String?) = … http://localhost:8080/foo

Slide 35

Slide 35 text

Custom DSL 35 nest( ), route( . andRoute( ). andNest( ), . . ); path("/blog").and(accept(TEXT_HTML) GET("/"), blogHandler::findAllView) GET("/{slug}"), blogHandler::findOneView) path("/api/blog").and(accept(APPLICATION_JSON) route(GET("/"), blogHandler::findAll) andRoute(GET("/{id}"), blogHandler::findOne) andRoute(POST("/"), blogHandler::create)

Slide 36

Slide 36 text

Custom DSL 36 router { ( ).nest { } ( ).nest { } } "/blog" and accept(TEXT_HTML) GET("/", blogHandler::findAllView) GET("/{slug}", blogHandler::findOneView) "/api/blog" and accept(APPLICATION_JSON) GET("/", blogHandler::findAll) GET("/{id}", blogHandler::findOne) POST("/", blogHandler::create)

Slide 37

Slide 37 text

And many more… 37 https://goo.gl/gMnhxN

Slide 38

Slide 38 text

@snicoll Demo

Slide 39

Slide 39 text

Try it today: start.spring.io 39

Slide 40

Slide 40 text

40 Thank You! @snicoll