Copenhagen Denmark
The state of Kotlin support in Spring Sébastien Deleuze
@sdeleuze
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
Copenhagen Denmark
@sdeleuze
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 
4 
6 
7 
8 
9 
1 
13 
14 
Reference documentation in Kotlin Spring Boot 2.2 Next year estimate Spring Framework Spring Boot Spring Data Spring Security
15 
17
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() }
18 
contract { request { url = url("/foo") method = PUT headers { header("foo", "bar") } body = body("foo" to "bar") } response { status = OK } }
19 
http { formLogin { loginPage = "/log-in" } authorizeRequests { authorize("/css/**", permitAll) authorize("/user/**", hasAuthority("ROLE_USER")) } }
https://github.com/spring-projects-experimental/spring-security-kotlin-dsl
21 
fun hello(request: ServerRequest): ServerResponse
22 
fun hello(request: ServerRequest) = ServerResponse.ok().body("Hello world!")
23 
router { GET("/hello", ::hello) } fun hello(request: ServerRequest) = ServerResponse.ok().body("Hello world!")
24 
@Configuration class RoutesConfiguration { @Bean fun routes(): RouterFunction<ServerResponse> = router { GET("/hello", ::hello) } fun hello(request: ServerRequest) = ServerResponse.ok().body("Hello world!") }
25 
@Component class PersonHandler(private val repository: PersonRepository) { fun listPeople(request: ServerRequest): ServerResponse { // ... } fun createPerson(request: ServerRequest): ServerResponse { // ... } fun getPerson(request: ServerRequest): ServerResponse { // ... } }
26 
@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) } }
27 
@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!") }
28 
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
30
31
32
33
34
35
36
37
38 
suspend fun hello() { delay(1000) println("Hello world!") }
39
* As explained by Bob Nystrom in its great blog post What Color is Your Function?
40
41 
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()) }
42
* kotlinx.coroutines.flow.Flow not java.util.concurrent.Flow
43
44
45
46 
interface Flow<T> { suspend fun collect(collector: FlowCollector<T>) } interface FlowCollector<T> { suspend fun emit(value: T) }
47 
fun <T> Flow<T>.filter(predicate: suspend (T) -> Boolean): Flow<T> = transform { value -> if (predicate(value)) return@transform emit(value) }
48 
val flow = flow { for (i in 1..3) { delay(100) emit(i) } }
49 
flow.filter { it < 2 }.map(::asyncOperation).collect()
51
* Redis, MongoDB, Cassandra, R2DBC
52
53
54 
@GetMapping("/api/banner") suspend fun suspendingEndpoint(): Banner { delay(10) return Banner("title", "Lorem ipsum") }
55 
@GetMapping("/banner") suspend fun render(model: Model): String { delay(10) model["banner"] = Banner("title", "Lorem ipsum") return "index" }
56
57 
@GetMapping("/banners") suspend fun flow(): Flow<Banner> = client.get() .uri("/messages") .accept(MediaType.TEXT_EVENT_STREAM) .retrieve() .bodyToFlow<String>() .map { Banner("title", it) }
58 
coRouter { GET("/hello", ::hello) } suspend fun hello(request: ServerRequest) = ServerResponse.ok().bodyValueAndAwait("Hello world!")
59 
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)
60 
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() } }
61 
class UserRepository(private val mongo: ReactiveFluentMongoOperations) { fun findAll(): Flow<User> = mongo.query<User>().flow() suspend fun findOne(id: String): User = mongo.query<User>() .matching(query(where("id").isEqualTo(id))).awaitOne() suspend fun insert(user: User): User = mongo.insert<User>().oneAndAwait(user) suspend fun update(user: User): User = mongo.update<User>().replaceWith(user) .asType<User>().findReplaceAndAwait() }
62 
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
63 
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
https://github.com/sdeleuze/spring-boot-coroutines-demo
64
66 
@ConfigurationProperties("blog") class BlogProperties { lateinit var title: String val banner = Banner() class Banner { var title: String? = null lateinit var content: String } }
67 
@ConstructorBinding @ConfigurationProperties("blog") data class BlogProperties(val title: String, val banner: Banner) { data class Banner(val title: String?, val content: String) }
68 
70
Explicit configuration for Spring Boot using Kotlin DSLs
71
Experimental
72
7 3
74 
val app = application(WebApplicationType.SERVLET) { beans { bean<SampleService>() bean<SampleHandler>() } webMvc { port = if (profiles.contains("test")) 8181 else 8080 router { val handler = ref<SampleHandler>() GET("/", handler::hello) GET("/api", handler::json) } converters { string() jackson() } } }
75
Discoverability via auto-complete instead of conventions
76
77
7 8 
https://github.com/spring-projects-experimental/spring-fu
80
My goal is to make Kofu the Spring Boot Kotlin DSL
81
8 3 
JIT GraalVM native CE GraalVM native EE
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
84
Spring Framework 5.3 should be able to configure reflection, proxies, resources, etc. dynamically with GraalVM native.
8 5 
https://spring.io/guides/tutorials/spring-boot-kotlin/
8 6 
https://youtu.be/BoidEr_ZCGc https://youtu.be/3eoAxphAUIg
#KotlinConf
Sébastien Deleuze @sdeleuze