Improve Kotlin documentation
This commit is contained in:
parent
cd69a4a03b
commit
ff1f368751
|
@ -333,54 +333,6 @@ when you need to register routes depending on dynamic data (for example, from a
|
||||||
See https://github.com/mixitconf/mixit/[MiXiT project] for a concrete example.
|
See https://github.com/mixitconf/mixit/[MiXiT project] for a concrete example.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
=== Coroutines
|
|
||||||
|
|
||||||
As of Spring Framework 5.2, https://kotlinlang.org/docs/reference/coroutines-overview.html[Coroutines] support
|
|
||||||
is provided via:
|
|
||||||
|
|
||||||
* https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/index.html[Deferred] and https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-flow/index.html[Flow] return value support in Spring WebFlux annotated `@Controller`
|
|
||||||
* Suspending function support in Spring WebFlux annotated `@Controller`
|
|
||||||
* Extensions for WebFlux {doc-root}/spring-framework/docs/{spring-version}/kdoc-api/spring-framework/org.springframework.web.reactive.function.client/index.html[client] and {doc-root}/spring-framework/docs/{spring-version}/kdoc-api/spring-framework/org.springframework.web.reactive.function.server/index.html[server] functional API.
|
|
||||||
* WebFlux.fn {doc-root}/spring-framework/docs/{spring-version}/kdoc-api/spring-framework/org.springframework.web.reactive.function.server/co-router.html[coRouter { }]
|
|
||||||
|
|
||||||
Coroutines extensions use `await` prefix or `AndAwait` suffix, and most are using similar
|
|
||||||
names to their reactive counterparts.
|
|
||||||
|
|
||||||
[source,kotlin,indent=0]
|
|
||||||
----
|
|
||||||
@Configuration
|
|
||||||
class RouterConfiguration {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
fun mainRouter(userHandler: UserHandler) = coRouter {
|
|
||||||
GET("/", userHandler::listView)
|
|
||||||
GET("/api/user", userHandler::listApi)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
----
|
|
||||||
|
|
||||||
[source,kotlin,indent=0]
|
|
||||||
----
|
|
||||||
class UserHandler(builder: WebClient.Builder) {
|
|
||||||
|
|
||||||
private val client = builder.baseUrl("...").build()
|
|
||||||
|
|
||||||
suspend fun listView(request: ServerRequest): ServerResponse =
|
|
||||||
ServerResponse.ok().renderAndAwait("users", mapOf("users" to
|
|
||||||
client.get().uri("...").awaitExchange().awaitBody<User>()))
|
|
||||||
|
|
||||||
suspend fun listApi(request: ServerRequest): ServerResponse =
|
|
||||||
ServerResponse.ok().contentType(MediaType.APPLICATION_JSON_UTF8).bodyAndAwait(
|
|
||||||
client.get().uri("...").awaitExchange().awaitBody<User>())
|
|
||||||
}
|
|
||||||
----
|
|
||||||
|
|
||||||
Read this blog post about https://medium.com/@elizarov/structured-concurrency-722d765aa952[structured concurrency]
|
|
||||||
to understand how to run code concurrently with Coroutines.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
=== MockMvc DSL
|
=== MockMvc DSL
|
||||||
|
|
||||||
A Kotlin DSL is provided via `MockMvc` Kotlin extensions in order to provide a more
|
A Kotlin DSL is provided via `MockMvc` Kotlin extensions in order to provide a more
|
||||||
|
@ -440,13 +392,182 @@ refactoring support in a supported IDE, as the following example shows:
|
||||||
"""
|
"""
|
||||||
----
|
----
|
||||||
|
|
||||||
NOTE: Kotlin Script Templates are not compatible yet with Spring Boot fatjar mechanism, see related
|
WARNING: Kotlin Script Templates support is experimental and not compatible yet with Spring Boot fatjar mechanism, see related
|
||||||
https://youtrack.jetbrains.com/issue/KT-21443[KT-21443] and https://youtrack.jetbrains.com/issue/KT-27956[KT-27956]
|
https://youtrack.jetbrains.com/issue/KT-21443[KT-21443] and https://youtrack.jetbrains.com/issue/KT-27956[KT-27956]
|
||||||
issues.
|
issues.
|
||||||
|
|
||||||
See the https://github.com/sdeleuze/kotlin-script-templating[kotlin-script-templating] example
|
See the https://github.com/sdeleuze/kotlin-script-templating[kotlin-script-templating] example
|
||||||
project for more details.
|
project for more details.
|
||||||
|
|
||||||
|
== Coroutines
|
||||||
|
|
||||||
|
Kotlin https://kotlinlang.org/docs/reference/coroutines-overview.html[Coroutines] are Kotlin
|
||||||
|
lightweight threads allowing to write non-blocking code in an imperative way. On language side,
|
||||||
|
suspending functions provides an abstraction for asynchronous operations while on library side
|
||||||
|
https://github.com/Kotlin/kotlinx.coroutines[kotlinx.coroutines] provides functions likes
|
||||||
|
https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/async.html[`async { }`]
|
||||||
|
and types like https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-flow/index.html[`Flow`].
|
||||||
|
|
||||||
|
Spring Framework provides support for Coroutines on the following scope:
|
||||||
|
|
||||||
|
* https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/index.html[Deferred] and https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-flow/index.html[Flow] return values support in Spring WebFlux annotated `@Controller`
|
||||||
|
* Suspending function support in Spring WebFlux annotated `@Controller`
|
||||||
|
* Extensions for WebFlux {doc-root}/spring-framework/docs/{spring-version}/kdoc-api/spring-framework/org.springframework.web.reactive.function.client/index.html[client] and {doc-root}/spring-framework/docs/{spring-version}/kdoc-api/spring-framework/org.springframework.web.reactive.function.server/index.html[server] functional API.
|
||||||
|
* WebFlux.fn {doc-root}/spring-framework/docs/{spring-version}/kdoc-api/spring-framework/org.springframework.web.reactive.function.server/co-router.html[coRouter { }] DSL
|
||||||
|
|
||||||
|
=== How Reactive translates to Coroutines?
|
||||||
|
|
||||||
|
For return values, the translation from Reactive to Coroutines APIs is the following:
|
||||||
|
|
||||||
|
* `fun handler(): Mono<Void>` becomes `suspend fun handler()`
|
||||||
|
* `fun handler(): Mono<T>` becomes `suspend fun handler(): T` or `suspend fun handler(): T?` depending on if the `Mono` can be empty or not (with the advantage of beeing more statically typed)
|
||||||
|
* `fun handler(): Flux<T>` becomes `fun handler(): Flow<T>`
|
||||||
|
|
||||||
|
For input parameters:
|
||||||
|
|
||||||
|
* If laziness is not needed, `fun handler(mono: Mono<T>)` becomes `fun handler(value: T)` since a suspending functions can be invoked to get the value parameter.
|
||||||
|
* If laziness is needed, `fun handler(mono: Mono<T>)` becomes `fun handler(supplier: () -> T)` or `fun handler(supplier: () -> T?)`
|
||||||
|
|
||||||
|
https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-flow/index.html[`Flow`] is `Flux` equivalent in Coroutines world, suitable for hot or cold stream, finite or infinite streams, with the following main differences:
|
||||||
|
|
||||||
|
* `Flow` is push-based while `Flux` is push-pull hybrid
|
||||||
|
* Backpressure is implemented via suspending functions
|
||||||
|
* `Flow` has only a https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-flow/collect.html[single suspending `collect` method] and operators are implemented as https://kotlinlang.org/docs/reference/extensions.html[extensions]
|
||||||
|
* https://github.com/Kotlin/kotlinx.coroutines/tree/master/kotlinx-coroutines-core/common/src/flow/operators[Operators are easy to implement] thanks to Coroutines and extensions allow to add custom ones easily to `Flow`
|
||||||
|
* Collect operations are suspending functions
|
||||||
|
* `map` operator supports asynchronous operation (no need for `flatMap`) since it takes a suspending function parameter
|
||||||
|
|
||||||
|
Read this blog post about https://medium.com/@elizarov/structured-concurrency-722d765aa952[structured concurrency]
|
||||||
|
to understand how to run code concurrently with Coroutines and how are managed exceptions and cancellations.
|
||||||
|
|
||||||
|
=== Controllers
|
||||||
|
|
||||||
|
Here is an example of a Coroutines `@RestController`.
|
||||||
|
|
||||||
|
[source,kotlin,indent=0]
|
||||||
|
----
|
||||||
|
@RestController
|
||||||
|
class CoroutinesRestController(client: WebClient, banner: Banner) {
|
||||||
|
|
||||||
|
@GetMapping("/suspend")
|
||||||
|
suspend fun suspendingEndpoint(): Banner {
|
||||||
|
delay(10)
|
||||||
|
return banner
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/flow")
|
||||||
|
fun flowEndpoint() = flow {
|
||||||
|
delay(10)
|
||||||
|
emit(banner)
|
||||||
|
delay(10)
|
||||||
|
emit(banner)
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/deferred")
|
||||||
|
fun deferredEndpoint() = GlobalScope.async {
|
||||||
|
delay(10)
|
||||||
|
banner
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/sequential")
|
||||||
|
suspend fun sequential(): List<Banner> {
|
||||||
|
val banner1 = client
|
||||||
|
.get()
|
||||||
|
.uri("/suspend")
|
||||||
|
.accept(MediaType.APPLICATION_JSON)
|
||||||
|
.awaitExchange()
|
||||||
|
.awaitBody<Banner>()
|
||||||
|
val banner2 = client
|
||||||
|
.get()
|
||||||
|
.uri("/suspend")
|
||||||
|
.accept(MediaType.APPLICATION_JSON)
|
||||||
|
.awaitExchange()
|
||||||
|
.awaitBody<Banner>()
|
||||||
|
return listOf(banner1, banner2)
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/parallel")
|
||||||
|
suspend fun parallel(): List<Banner> = coroutineScope {
|
||||||
|
val deferredBanner1: Deferred<Banner> = async {
|
||||||
|
client
|
||||||
|
.get()
|
||||||
|
.uri("/suspend")
|
||||||
|
.accept(MediaType.APPLICATION_JSON)
|
||||||
|
.awaitExchange()
|
||||||
|
.awaitBody<Banner>()
|
||||||
|
}
|
||||||
|
val deferredBanner2: Deferred<Banner> = async {
|
||||||
|
client
|
||||||
|
.get()
|
||||||
|
.uri("/suspend")
|
||||||
|
.accept(MediaType.APPLICATION_JSON)
|
||||||
|
.awaitExchange()
|
||||||
|
.awaitBody<Banner>()
|
||||||
|
}
|
||||||
|
listOf(deferredBanner1.await(), deferredBanner2.await())
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/error")
|
||||||
|
suspend fun error(): ServerResponse {
|
||||||
|
throw IllegalStateException()
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/cancel")
|
||||||
|
suspend fun cancel(): ServerResponse {
|
||||||
|
throw CancellationException()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
View rendering with a `@Controller` is also supported.
|
||||||
|
|
||||||
|
[source,kotlin,indent=0]
|
||||||
|
----
|
||||||
|
@Controller
|
||||||
|
class CoroutinesViewController(banner: Banner) {
|
||||||
|
|
||||||
|
@GetMapping("/")
|
||||||
|
suspend fun render(model: Model): String {
|
||||||
|
delay(10)
|
||||||
|
model["banner"] = banner
|
||||||
|
return "index"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
=== WebFlux.fn
|
||||||
|
|
||||||
|
Here is an example of Coroutines router definined via the {doc-root}/spring-framework/docs/{spring-version}/kdoc-api/spring-framework/org.springframework.web.reactive.function.server/co-router.html[coRouter { }] DSL and related handlers.
|
||||||
|
|
||||||
|
[source,kotlin,indent=0]
|
||||||
|
----
|
||||||
|
@Configuration
|
||||||
|
class RouterConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
fun mainRouter(userHandler: UserHandler) = coRouter {
|
||||||
|
GET("/", userHandler::listView)
|
||||||
|
GET("/api/user", userHandler::listApi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
[source,kotlin,indent=0]
|
||||||
|
----
|
||||||
|
class UserHandler(builder: WebClient.Builder) {
|
||||||
|
|
||||||
|
private val client = builder.baseUrl("...").build()
|
||||||
|
|
||||||
|
suspend fun listView(request: ServerRequest): ServerResponse =
|
||||||
|
ServerResponse.ok().renderAndAwait("users", mapOf("users" to
|
||||||
|
client.get().uri("...").awaitExchange().awaitBody<User>()))
|
||||||
|
|
||||||
|
suspend fun listApi(request: ServerRequest): ServerResponse =
|
||||||
|
ServerResponse.ok().contentType(MediaType.APPLICATION_JSON_UTF8).bodyAndAwait(
|
||||||
|
client.get().uri("...").awaitExchange().awaitBody<User>())
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue