the state of kotlin support in spring s bastien deleuze
play

The state of Kotlin support in Spring Sbastien Deleuze @sdeleuze - PowerPoint PPT Presentation

The state of Kotlin support in Spring Sbastien 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


  1. The state of Kotlin support in Spring Sébastien Deleuze @sdeleuze Copenhagen Denmark

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

  3. Less flights 3 

  4. Slower but more surprises ;-) 4 

  5. Why Kotlin?

  6. Improve signal to noise ratio 6 

  7. Safety Improve the signal to noise ratio of the code 7 

  8. Discoverability Improve the signal to noise ratio of the code 8 

  9. Pleasure 9 

  10. Android 1 0 

  11. Spring Kotlin

  12. How much?

  13. Framework documentation in Kotlin! 13 

  14. Status Reference documentation in Kotlin Spring Boot 2.2 Next year estimate Spring Framework Spring Boot Spring Data Spring Security 14 

  15. Gradle Kotlin DSL on start.spring.io 15 

  16. More DSLs

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

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

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

  20. Spring MVC DSL and functional API

  21. Handler API fun hello(request: ServerRequest): ServerResponse 21 

  22. Handler implementation fun hello(request: ServerRequest) = ServerResponse.ok().body( "Hello world!" ) 22 

  23. Router router { GET( "/hello" , ::hello) } fun hello(request: ServerRequest) = ServerResponse.ok().body( "Hello world!" ) 23 

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

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

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

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

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

  29. Coroutines

  30. Coroutines are lightweight threads (you can create 100.000s of them) 30

  31. The foundations are in Kotlin language 31

  32. But most of the implementation lives on library side in kotlinx.coroutines 32

  33. Coroutines allows to consume Spring Reactive stack with a nice balance between imperative and declarative style 33

  34. Operations are sequential by default 34

  35. Concurrency is explicit 35

  36. 3 main building blocks you need to know 36

  37. Suspending function 37

  38. Suspending functions suspend fun hello() { delay (1000) println ( "Hello world!" ) } 38 

  39. Note: suspending functions color your API* * As explained by Bob Nystrom in its great blog post What Color is Your Function? 39

  40. Structured concurrency 40

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

  42. Flow<T> * * kotlinx.coroutines.flow.Flow not java.util.concurrent.Flow 42

  43. Coroutines 1.3 introduces a new Reactive type: Flow<T> 43

  44. Flow is the equivalent of Flux in Coroutines world 44

  45. Flow is interoperable with Reactive Streams and supports backpressure 45

  46. Flow API interface Flow<T> { suspend fun collect(collector: FlowCollector<T>) } interface FlowCollector<T> { suspend fun emit(value: T) } 46 

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

  48. Create a Flow val flow = flow { for (i in 1..3) { delay (100) emit(i) } } 48 

  49. Consume a Flow flow. filter { it < 2 } . map (::asyncOperation). collect () 49 

  50. What about Spring support for Coroutines?

  51. Spring provides official Coroutines support for Spring WebFlux, Data*, Vault, RSocket * Redis, MongoDB, Cassandra, R2DBC 51

  52. No new types introduced 52

  53. Seamless support with the annotation programming model 53

  54. WebFlux suspending handler method @GetMapping( "/api/banner" ) suspend fun suspendingEndpoint(): Banner { delay (10) return Banner( "title" , "Lorem ipsum" ) } 54 

  55. WebFlux suspending view rendering @GetMapping( "/banner" ) suspend fun render(model: Model): String { delay (10) model[ "banner" ] = Banner( "title" , "Lorem ipsum" ) return "index" } 55 

  56. Reactive types extensions for Coroutines APIs 56

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

  58. WebFlux Router coRouter { GET( "/hello" , ::hello) } suspend fun hello(request: ServerRequest) = ServerResponse.ok(). bodyValueAndAwait ( "Hello world!" ) 58 

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

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