Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Spring Framework 5 ‣ Reactive foundations ‣ Spring WebFlux ‣ Kotlin builtin support ‣ Functional bean registration ‣ Baseline upgrade & cleanup ‣ Java 9 support ‣ Performance improvements 2
Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Why going Reactive? 4 More for scalability and stability than for speed
Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ What issue are we trying to solve? 5 1 request = 1 thread
Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ From blocking to event-based 6 Neutral to latency
Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Reactive Streams 7 Publisher Subscriber Backpressure request(n) data 0..N data then 0..1 (Error | Complete)
Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Spring Framework 5 uses Reactor for its Reactive foundations Reactor, RxJava, Akka Streams, etc. 12 Reactive libraries Your web application can use
Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Flux<T> • Implements Reactive Streams Publisher • 0 to n elements • Operators: flux.map(…).zip(…).flatMap(…) 13
Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Mono<T> • Implements Reactive Streams Publisher • 0 to 1 element • Operators: mono.then(…).otherwise(…) 14
Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ StepVerifier 15 StepVerifier.create(flux) .expectNext("foo", "bar") .verifyComplete(); ‣ Designed to test easily Reactive Streams Publishers ‣ Carefully designed after writing thousands of Reactor and Spring WebFlux tests
Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Blocking versus Reactive API 16 interface ReactiveUserRepository { Mono<User> findOne(String id); Flux<User> findAll(); Mono<Void> save(Mono<User> user); } interface UserRepository { User findOne(String id); List<User> findAll(); void save(User user); } Blocking API Reactive API ‣ Method returns when all the data has been received ‣ Exceptions thrown when an error occurs ‣ Method returns immediately ‣ Error event instead of throwing exception ‣ Complete event instead of method return ‣ Mono<Void> = error or success, no data ‣ Data comes as events too the Mono or Flux return value
Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Upcoming high-level features 17
Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Spring MVC 20 @RestController public class UserController { private final UserRepository repo; public UserController(UserRepository repo) { this.repo = repo; } @GetMapping("/user/{id}") public User findOne(@PathVariable String id) { return repo.findOne(id); } @GetMapping("/user") public List<User> findAll() { return repo.findAll(); } @PostMapping("/user") public void save(@RequestBody User user) { repo.save(user); } } interface UserRepository { User findOne(String id); List<User> findAll(); void save(User user); }
Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ WebFlux functional API 23 ‣ Reactive ‣ Client and server ‣ Lightweight ‣ Flexible ‣ Can be use with or without dependency injection! ‣ Simple functional building blocks
Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ WebFlux functional server API 25 @Controller public class UserController { public RouterFunction<ServerResponse> route() { return RouterFunctions .route(GET("/users"), this::listUsers) .andRoute(POST("/users"), this::createUser); } Mono<ServerResponse> listUsers(ServerRequest request) { ... } Mono<ServerResponse> createUser(ServerRequest request) { ... } }
Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ WebFlux Streaming Showcase 29 https://github.com/bclozel/webflux-streaming-showcase quote-stream streaming-service GET /quotes "application/stream+json" GET /quotes/feed "text/event-stream" MongoDB
Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Kotlin ‣ Overcome Java limitations ‣ Elegant and pragmatic language ‣ Concise code ‣ Simple and easy to learn ‣ Very good Java interoperability ‣ Embrace both functional and object oriented programming 31
Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ How does it compare with … 32 Same conciseness and expressive code, but Kotlin default static typing and null-safety make a big difference. "Kotlin is a software engineering language in contrast to Scala which is a computing science language." Eric Kolotyluk, Electronic Arts, Scala developer Swift Swift and Kotlin share similar syntax. Swift is coming from native world while Kotlin is coming from JVM world.
Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Kotlin is much more than a "super Java" or than Lombok 34
Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ My favorite Kotlin features ‣ Awesome type inference with static typing ‣ Extensions ‣ Reified type parameters ‣ Null safety ‣ Type aliases ‣ Allows to write nice DSL ‣ Smart casts 35
Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Spring Initializr 36 https://start.spring.io/#!language=kotlin
Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Introducing Kotlin support in Spring Framework 5 37 https://goo.gl/gMnhxN
Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Gradle build files written in Kotlin 38
Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ kotlin-spring Gradle and Maven plugin 39 @SpringBootApplication open class Application { @Bean open fun foo() = ... @Bean open fun bar() = ... } Without kotlin-spring @SpringBootApplication class Application { @Bean fun foo() = ... @Bean fun bar() = ... } With kotlin-spring Also available as a kotlin-allopen generic plugin
Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ kotlin-noarg Gradle and Maven plugin 40 configure<NoArgExtension> { annotation("org.springframework.data.mongodb.core.mapping.Document") } Also available preconfigured for JPA as kotlin-jpa @Document data class User( @Id val login: String, val firstname: String, val lastname: String, val email: String, val company: String? = null, val description: Map<Language, String> = emptyMap(), val logoUrl: String? = null, val role: Role = Role.ATTENDEE)
Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Persistence 41 class EventRepository(val template: ReactiveMongoTemplate) { fun findAll() = template.find<Event>(Query().with(Sort("year"))) fun findOne(id: String) = template.findById<Event>(id) fun deleteAll() = template.remove<Event>(Query()) fun save(event: Event) = template.save(event) }
Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Extensions 42 // Spring Boot extensions fun run(type: KClass<*>, vararg args: String) = SpringApplication.run(type.java, *args) // Spring Data extensions inline fun <reified T : Any> ReactiveMongoOperations.findById(id: Any): Mono<T> = findById(id, T::class.java) inline fun <reified T : Any> ReactiveMongoOperations.find(query: Query): Flux<T> = find(query, T::class.java) inline fun <reified T : Any> ReactiveMongoOperations.findAll(): Flux<T> = findAll(T::class.java) inline fun <reified T : Any> ReactiveMongoOperations.findOne(query: Query): Mono<T> = find(query, T::class.java).next() inline fun <reified T : Any> ReactiveMongoOperations.remove(query: Query): Mono<DeleteResult> = remove(query, T::class.java) // Other extensions fun ServerRequest.language() = Language.findByTag(this.headers().header(HttpHeaders.ACCEPT_LANGUAGE).first())
Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Spring MVC 43 @RestController class UserController(val repo: UserRepository) { @GetMapping("/user/{id}") fun findOne(@PathVariable id: String) = repo.findOne(id) @GetMapping("/user") fun findAll() = repo.findAll() @PostMapping("/user") fun save(@RequestBody user: User) = repo.save(user) } interface UserRepository { fun findOne(id: String): User fun findAll(): List<User> fun save(user: User) } Classes and functions are public by default Static typing + type inference Constructor injection without @Autowired if single constructor
Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Spring WebFlux annotation-based 44 @RestController class ReactiveUserController(val repository: ReactiveUserRepository) { @GetMapping("/user/{id}") fun findOne(@PathVariable id: String) = repository.findOne(id) @GetMapping("/user") fun findAll() = repository.findAll() @PostMapping("/user") fun save(@RequestBody user: Mono<User>) = repository.save(user) } interface ReactiveUserRepository { fun findOne(id: String): Mono<User> fun findAll(): Flux<User> fun save(user: Mono<User>): Mono<Void> }
Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Spring WebFlux functional Kotlin DSL 49 router { ("/blog" and accept(TEXT_HTML)).nest { GET("/", blogController::findAllView) GET("/{slug}", blogController::findOneView) } ("/api/blog" and accept(APPLICATION_JSON)).nest { GET("/", blogController::findAll) GET("/{id}", blogController::findOne) POST("/", blogController::create) } } Rich set of RequestPredicate
Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Leveraging Kotlin nullable information 50 // "GET /foo" and "GET /foo?bar=baz" are allowed @GetMapping("/foo") fun foo(@RequestParam bar: String?) = ... // "GET /foo?bar=baz" is allowed and "GET /foo" will return an error @GetMapping("/foo") fun foo(@RequestParam bar: String) = ...
Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Spring Kotlin extensions 51 Mono<ServerResponse> findAll(ServerRequest request) { return ServerResponse.ok().body(repository.findAll(), User.class); } fun findAll(req: ServerRequest) = ok().body(repository.findAll()) No need to specify explicitly types thanks to Kotlin reified type parameters inline fun <reified T : Any> ServerResponse.BodyBuilder.body(publisher: Publisher<T>) = body(publisher, T::class.java) ServerResponseExtensions provided in spring-webflux.jar
Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Spring Framework extensions 52 ‣ ApplicationContext ‣ Spring WebFlux • Client • Server ‣ Spring MVC ‣ RestTemplate ‣ JDBC ‣ Reactor (via reactor-kotlin)
Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Kotlin type-safe templates 53 import io.spring.demo.* """ ${include("header")} <h1>${i18n("title")}</h1> <ul> ${users.joinToLine{ "<li>${i18n("user")} ${it.firstname} ${it.lastname}</li>" }} </ul> ${include("footer")} """ See https://github.com/sdeleuze/kotlin-script-templating for more details ‣ Regular Kotlin code, no new dialect to learn ‣ Extensible, refactoring and auto-complete support ‣ Need to cache compiled scripts for good performances
Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Tests with JUnit 54 class UserIntegrationTests : AbstractIntegrationTests() { @Test fun `Find Dan North`() { val speaker = client.get() .uri("/api/speaker/tastapod") .accept(APPLICATION_JSON) .exchange().then { r -> r.bodyToMono<User>() } StepVerifier.create(speaker) .consumeNextWith { assertEquals("North", it.lastname) assertTrue(it.role == Role.SPEAKER) } .verifyComplete() } }
Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ JUnit 5 will improve Kotlin support 55
Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ MiXiT reference web application 56 https://github.com/mixitconf/mixit
Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Functional bean registration API ‣ After XML and JavaConfig, a third major way to register your beans ‣ Lambda with Supplier act as a FactoryBean ‣ Very efficient, no reflection, no CGLIB proxies involved ‣ Usable with pure Spring Framework apps, not yet with Spring Boot 58
Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ API example 59 GenericApplicationContext context = new GenericApplicationContext(); context.registerBean(A.class); context.registerBean(B.class, () -> new B(context.getBean(A.class))); GenericApplicationContext { registerBean<A>() registerBean { B(it.getBean<A>()) }}
Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Using Jigsaw with Spring ‣ Spring Framework 5 jars are Jigsaw-compliant modules out of the box • Defined as “automatic modules” • Required to be able to access your code (classpath scanning, etc) ‣ Naming convention based on Maven metadata: • spring-context-5.0.0.RELEASE.jar ==> spring.context ‣ Example 64 module my.app.db { requires spring.jdbc; }
Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Performance improvements ‣ Functional bean registration is very fast • No reflection • No proxy ‣ Alternative to component scanning • Pre-computed index at compilation time • Extensible using @Indexed ‣ ASM meta-data cache ‣ Rewrite of AntPathMatcher ‣ Zero-copy transfer of org.springframework.core.io.Resource 65 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Indexed public @interface Component {
Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Spring Framework 5.0 roadmap ‣ M5 release available ‣ RC1 April 2017 ‣ GA June 2017 67
Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Spring Boot 2.0 (expected November 2017) ‣ Leverage Spring Framework 5.0 ‣ Spring WebFlux support ‣ Spring WebFlux Actuators ‣ Reactive support (data, security…) ‣ Improved Kotlin support ‣ … 68
Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Reactive Spring ongoing effort 69 ‣ Reactor Core, Netty, Adapter, Test are GA ‣ Reactor Kafka Milestone available ‣ Reactive support currently introduced in the whole Spring portfolio: • Spring Boot • Spring Data • Spring Security • Spring Cloud Stream • etc.
Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Try it: start.spring.io 70