Upgrade to Pro — share decks privately, control downloads, hide ads and more …

The state of Kotlin support in Spring

The state of Kotlin support in Spring

Sébastien Deleuze

October 19, 2019
Tweet

More Decks by Sébastien Deleuze

Other Decks in Programming

Transcript

  1. The state of
    Kotlin support in Spring
    Sébastien Deleuze
    October 19, 2019

    View full-size slide

  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.

    View full-size slide

  3. 3

    Less flights

    View full-size slide

  4. 5

    Improve signal to noise ratio

    View full-size slide

  5. 6

    Improve the signal to noise ratio
    of the code
    Safety

    View full-size slide

  6. 7

    Improve the signal to noise ratio
    of the code
    Discoverability

    View full-size slide

  7. 8

    Pleasure

    View full-size slide

  8. Spring Kotlin

    View full-size slide

  9. Framework documentation in Kotlin!
    12

    View full-size slide

  10. 13

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

    View full-size slide

  11. Gradle Kotlin DSL on start.spring.io
    14

    View full-size slide

  12. 16

    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()
    }
    MockMvc DSL by Clint Checketts and JB Nizet

    View full-size slide

  13. 17
    contract {
    request {
    url = url("/foo")
    method = PUT
    headers {
    header("foo", "bar")
    }
    body = body("foo" to "bar")
    }
    response {
    status = OK
    }
    }
    Spring Cloud Contract DSL by Tim Ysewyn

    View full-size slide

  14. 18

    http {
    formLogin {
    loginPage = "/log-in"
    }
    authorizeRequests {
    authorize("/css/**", permitAll)
    authorize("/user/**", hasAuthority("ROLE_USER"))
    }
    }
    Spring Security DSL by Eleftheria Stein
    https://github.com/spring-projects-experimental/spring-security-kotlin-dsl

    View full-size slide

  15. Spring MVC DSL and functional API

    View full-size slide

  16. 20

    fun hello(request: ServerRequest): ServerResponse
    Handler API

    View full-size slide

  17. 21

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

    View full-size slide

  18. 22

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

    View full-size slide

  19. 23

    @Configuration
    class RoutesConfiguration {
    @Bean
    fun routes(): RouterFunction = router {
    GET("/hello", ::hello)
    }
    fun hello(request: ServerRequest) =
    ServerResponse.ok().body("Hello world!")
    }
    Expose the router to Spring Boot

    View full-size slide

  20. 24

    @Component
    class PersonHandler(private val repository: PersonRepository) {
    fun listPeople(request: ServerRequest): ServerResponse {
    // ...
    }
    fun createPerson(request: ServerRequest): ServerResponse {
    // ...
    }
    fun getPerson(request: ServerRequest): ServerResponse {
    // ...
    }
    }
    A more realistic handler

    View full-size slide

  21. 25

    @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)
    }
    }
    A more realistic router

    View full-size slide

  22. 26

    @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!")
    }
    You can even create routes dynamically

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  25. The foundations are in Kotlin language
    30

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  28. Operations are sequential by default
    33

    View full-size slide

  29. Concurrency is explicit
    34

    View full-size slide

  30. 3 main building blocks you need to know
    35

    View full-size slide

  31. Suspending function
    36

    View full-size slide

  32. 37

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

    View full-size slide

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

    View full-size slide

  34. Structured concurrency
    39

    View full-size slide

  35. 40

    suspend fun loadAndCombine(name1: String, name2: String) = coroutineScope {
    val deferred1: Deferred = async { loadImage(name1) }
    val deferred2: Deferred = async { loadImage(name2) }
    combineImages(deferred1.await(), deferred2.await())
    }
    Structured concurrency

    View full-size slide

  36. Flow*
    41
    * kotlinx.coroutines.flow.Flow not java.util.concurrent.Flow

    View full-size slide

  37. Coroutines 1.3 introduces a new Reactive type:
    Flow
    42

    View full-size slide

  38. Flow is the equivalent of Flux in Coroutines
    world
    43

    View full-size slide

  39. Flow is interoperable with Reactive Streams and
    supports backpressure
    44

    View full-size slide

  40. 45

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

    View full-size slide

  41. 46

    fun Flow.filter(predicate: suspend (T) -> Boolean): Flow =
    transform { value ->
    if (predicate(value)) return@transform emit(value)
    }
    Operators as extensions easy to implement

    View full-size slide

  42. 47

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

    View full-size slide

  43. 48

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

    View full-size slide

  44. What about Spring support for
    Coroutines?

    View full-size slide

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

    View full-size slide

  46. No new types introduced
    51

    View full-size slide

  47. Seamless support with the annotation
    programming model
    52

    View full-size slide

  48. 53

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

    View full-size slide

  49. 54

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

    View full-size slide

  50. Reactive types extensions for Coroutines APIs
    55

    View full-size slide

  51. 56

    @GetMapping("/banners")
    suspend fun flow(): Flow = client.get()
    .uri("/messages")
    .accept(MediaType.TEXT_EVENT_STREAM)
    .retrieve()
    .bodyToFlow()
    .map { Banner("title", it) }
    WebClient

    View full-size slide

  52. 57

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

    View full-size slide

  53. 58

    private val requester: RSocketRequester = ...
    @MessageMapping("locate.radars.within")
    fun findRadars(request: MapRequest): Flow = requester
    .route("locate.radars.within")
    .data(request.viewBox)
    .retrieveFlow()
    .take(request.maxRadars)
    RSocket

    View full-size slide

  54. 59

    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().table("users").using(user).await()
    }
    }
    Spring Data R2DBC with transactions

    View full-size slide

  55. 60

    class UserRepository(private val mongo: ReactiveFluentMongoOperations) {
    fun findAll(): Flow =
    mongo.query().flow()
    suspend fun findOne(id: String): User = mongo.query()
    .matching(query(where("id").isEqualTo(id))).awaitOne()
    suspend fun insert(user: User): User =
    mongo.insert().oneAndAwait(user)
    suspend fun update(user: User): User =
    mongo.update().replaceWith(user)
    .asType().findReplaceAndAwait()
    }
    Spring Data MongoDB

    View full-size slide

  56. 61

    Coroutines are now the default way to go Reactive in Kotlin
    Coroutines dependency added
    by default on start.spring.io
    WebFlux & RSocket Kotlin code samples
    provided with Coroutines API

    View full-size slide

  57. 62

    Status
    Coroutines support Spring Boot 2.2 Next year estimate
    Spring WebFlux functional APIs
    Spring WebFlux WebClient
    Spring WebFlux @RequestMapping
    RSocket @MessageMapping
    Spring Data Reactive APIs
    Functional transactions
    Spring MVC @RequestMapping
    Spring Data repositories (DATACMNS-1508)
    @Transactional

    View full-size slide

  58. https://github.com/sdeleuze/spring-boot-coroutines-demo
    63

    View full-size slide

  59. 65

    @ConfigurationProperties("blog")
    class BlogProperties {
    lateinit var title: String
    val banner = Banner()
    class Banner {
    var title: String? = null
    lateinit var content: String
    }
    }
    @ConfigurationProperties

    View full-size slide

  60. 66

    @ConstructorBinding
    @ConfigurationProperties("blog")
    data class BlogProperties(val title: String, val banner: Banner) {
    data class Banner(val title: String?, val content: String)
    }
    @ConfigurationProperties + @ConstructorBinding

    View full-size slide

  61. 67

    start.spring.io helps to share Kotlin projects

    View full-size slide

  62. Kofu: the mother of all DSLs

    View full-size slide

  63. application { }
    69

    View full-size slide

  64. Explicit configuration for Spring Boot using Kotlin DSLs
    70

    View full-size slide

  65. Experimental
    71

    View full-size slide

  66. Leverage existing DSLs : beans, router, security
    7
    2

    View full-size slide

  67. 73

    val app = application(WebApplicationType.SERVLET) {
    beans {
    bean()
    bean()
    }
    webMvc {
    port = if (profiles.contains("test")) 8181 else 8080
    router {
    val handler = ref()
    GET("/", handler::hello)
    GET("/api", handler::json)
    }
    converters {
    string()
    jackson()
    }
    }
    }
    Spring Boot configured with Kofu DSL

    View full-size slide

  68. Composable configurations
    74

    View full-size slide

  69. Discoverability via auto-complete instead of conventions
    75

    View full-size slide

  70. Faster startup, less memory consumption
    76

    View full-size slide

  71. 77

    Faster startup

    View full-size slide

  72. https://github.com/spring-projects-experimental/spring-fu
    79

    View full-size slide

  73. My hope is to make Kofu the Spring Boot Kotlin DSL
    80

    View full-size slide

  74. GraalVM Native

    View full-size slide

  75. https://github.com/spring-projects-experimental/spring-graal-native
    Kotlin with Spring MVC or Spring WebFlux works on GraalVM native
    Coroutines support is broken due to graal#366
    82

    View full-size slide

  76. Thanks
    Twitter: @sdeleuze
    Credits to ibrandify and flaticon for some icons used

    View full-size slide