The state of Kotlin support in Spring Sébastien Deleuze @sdeleuze Copenhagen Denmark
Safe Harbor Statement The following is intended to outline the general direction of Pivotal's offerings. It is intended for information purposes only and may not be incorporated into any contract. Any information regarding pre-release of Pivotal offerings, future updates or other planned modifications is subject to ongoing evaluation by Pivotal and is subject to change. This information is provided without warranty or any kind, express or implied, and is not a commitment to deliver any material, code, or functionality, and should not be relied upon in making purchasing decisions regarding Pivotal's offerings. These purchasing decisions should only be based on features currently available. The development, release, and timing of any features or functionality described for Pivotal's offerings in this presentation remain at the sole discretion of Pivotal. Pivotal has no obligation to update forward looking information in this presentation.
Less flights 3 
Slower but more surprises ;-) 4 
Why Kotlin?
Improve signal to noise ratio 6 
Safety Improve the signal to noise ratio of the code 7 
Discoverability Improve the signal to noise ratio of the code 8 
Pleasure 9 
Android 1 0 
Spring Kotlin
How much?
Framework documentation in Kotlin! 13 
Status Reference documentation in Kotlin Spring Boot 2.2 Next year estimate Spring Framework Spring Boot Spring Data Spring Security 14 
Gradle Kotlin DSL on start.spring.io 15 
More DSLs
MockMvc DSL by Clint Checketts and JB Nizet mockMvc . request (HttpMethod. GET , "/person/{name}" , "Lee" ) { secure = true accept = APPLICATION_JSON headers { contentLanguage = Locale. FRANCE } principal = Principal { "foo" } } .andExpect { status { isOk } content { contentType( APPLICATION_JSON ) } jsonPath( "$.name" ) { value( "Lee" ) } content { json( """{"someBoolean": false}""" , false ) } } .andDo { print() } 17 
Spring Cloud Contract DSL by Tim Ysewyn contract { request { url = url( "/foo" ) method = PUT headers { header( "foo" , "bar" ) } body = body( "foo" to "bar" ) } response { status = OK } } 18 
Spring Security DSL by Eleftheria Stein https://github.com/spring-projects-experimental/spring-security-kotlin-dsl http { formLogin { loginPage = "/log-in" } authorizeRequests { authorize( "/css/**" , permitAll ) authorize( "/user/**" , hasAuthority( "ROLE_USER" )) } } 19 
Spring MVC DSL and functional API
Handler API fun hello(request: ServerRequest): ServerResponse 21 
Handler implementation fun hello(request: ServerRequest) = ServerResponse.ok().body( "Hello world!" ) 22 
Router router { GET( "/hello" , ::hello) } fun hello(request: ServerRequest) = ServerResponse.ok().body( "Hello world!" ) 23 
Expose the router to Spring Boot @Configuration class RoutesConfiguration { @Bean fun routes(): RouterFunction<ServerResponse> = router { GET( "/hello" , ::hello) } fun hello(request: ServerRequest) = ServerResponse.ok().body( "Hello world!" ) } 24 
A more realistic handler @Component class PersonHandler( private val repository : PersonRepository) { fun listPeople(request: ServerRequest): ServerResponse { // ... } fun createPerson(request: ServerRequest): ServerResponse { // ... } fun getPerson(request: ServerRequest): ServerResponse { // ... } } 25 
A more realistic router @Configuration class RouteConfiguration { @Bean fun routes(handler: PersonHandler) = router { accept( APPLICATION_JSON ). nest { GET( "/person/{id}" , handler::getPerson) GET( "/person" , handler::listPeople) } POST( "/person" , handler::createPerson) } } 26 
You can even create routes dynamically @Configuration class RouteConfiguration { @Bean fun routes(routeRepository: RouteRepository) = router { for (route in routeRepository.listRoutes()) { GET( "/$ route " ) { request -> hello(request, route) } } } fun hello(request: ServerRequest, message: String) = ServerResponse.ok().body( "Hello $ message !" ) } 27 
Status Kotlin DSLs Spring Boot 2.1 Spring Boot 2.2 Next year estimate Beans DSL WebFlux router DSL WebFlux Coroutines router DSL WebMvc router DSL MockMvc DSL Spring Security DSL 28 
Coroutines
Coroutines are lightweight threads (you can create 100.000s of them) 30
The foundations are in Kotlin language 31
But most of the implementation lives on library side in kotlinx.coroutines 32
Coroutines allows to consume Spring Reactive stack with a nice balance between imperative and declarative style 33
Operations are sequential by default 34
Concurrency is explicit 35
3 main building blocks you need to know 36
Suspending function 37
Suspending functions suspend fun hello() { delay (1000) println ( "Hello world!" ) } 38 
Note: suspending functions color your API* * As explained by Bob Nystrom in its great blog post What Color is Your Function? 39
Structured concurrency 40
Structured concurrency suspend fun loadAndCombine(name1: String, name2: String) = coroutineScope { val deferred1: Deferred<Image> = async { loadImage(name1) } val deferred2: Deferred<Image> = async { loadImage(name2) } combineImages(deferred1.await(), deferred2.await()) } 41 
Flow<T> * * kotlinx.coroutines.flow.Flow not java.util.concurrent.Flow 42
Coroutines 1.3 introduces a new Reactive type: Flow<T> 43
Flow is the equivalent of Flux in Coroutines world 44
Flow is interoperable with Reactive Streams and supports backpressure 45
Flow API interface Flow<T> { suspend fun collect(collector: FlowCollector<T>) } interface FlowCollector<T> { suspend fun emit(value: T) } 46 
Operators as extensions easy to implement fun <T> Flow<T>.filter(predicate: suspend (T) -> Boolean): Flow<T> = transform { value -> if (predicate(value)) return @transform emit(value) } 47 
Create a Flow val flow = flow { for (i in 1..3) { delay (100) emit(i) } } 48 
Consume a Flow flow. filter { it < 2 } . map (::asyncOperation). collect () 49 
What about Spring support for Coroutines?
Spring provides official Coroutines support for Spring WebFlux, Data*, Vault, RSocket * Redis, MongoDB, Cassandra, R2DBC 51
No new types introduced 52
Seamless support with the annotation programming model 53
WebFlux suspending handler method @GetMapping( "/api/banner" ) suspend fun suspendingEndpoint(): Banner { delay (10) return Banner( "title" , "Lorem ipsum" ) } 54 
WebFlux suspending view rendering @GetMapping( "/banner" ) suspend fun render(model: Model): String { delay (10) model[ "banner" ] = Banner( "title" , "Lorem ipsum" ) return "index" } 55 
Reactive types extensions for Coroutines APIs 56
WebClient @GetMapping( "/banners" ) suspend fun flow(): Flow<Banner> = client .get() .uri( "/messages" ) .accept(MediaType. TEXT_EVENT_STREAM ) .retrieve() . bodyToFlow <String>() . map { Banner( "title" , it ) } 57 
WebFlux Router coRouter { GET( "/hello" , ::hello) } suspend fun hello(request: ServerRequest) = ServerResponse.ok(). bodyValueAndAwait ( "Hello world!" ) 58 
RSocket private val requester : RSocketRequester = ... @MessageMapping( "locate.radars.within" ) fun findRadars(request: MapRequest): Flow<Radar> = requester .route( "locate.radars.within" ) .data(request. viewBox ) . retrieveFlow <AirportLocation>() . take (request. maxRadars ) 59 
Spring Data R2DBC with transactions class PersonRepository( private val client : DatabaseClient, private val operator : TransactionalOperator) { suspend fun initDatabase() = operator . executeAndAwait { save(User( "smaldini" , "Stéphane" , "Maldini" )) save(User( "sdeleuze" , "Sébastien" , "Deleuze" )) save(User( "bclozel" , "Brian" , "Clozel" )) } suspend fun save(user: User) { client .insert(). into <User>().table( "users" ).using(user). await () } } 60 
Recommend
More recommend