В предыдущем сообщении в блоге я описал, как Spring Webflux, поддержка реактивного программирования в Spring Web Framework, использует DSL на основе Kotlin, чтобы пользователи могли описывать маршруты очень интуитивно понятным способом. Здесь я хотел бы немного изучить базовую реализацию.
Пример DSL, описывающий набор конечных точек, выглядит следующим образом:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
package sample.routes import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.http.MediaType.APPLICATION_JSON import org.springframework.web.reactive.function.server.router import sample.handler.MessageHandler @Configuration class AppRoutes( private val messageHandler: MessageHandler) { @Bean fun apis() = router { (accept(APPLICATION_JSON) and "/messages" ).nest { GET( "/" , messageHandler::getMessages) POST( "/" , messageHandler::addMessage) GET( "/{id}" , messageHandler::getMessage) PUT( "/{id}" , messageHandler::updateMessage) DELETE( "/{id}" , messageHandler::deleteMessage) } } } |
Чтобы проанализировать образец, позвольте мне начать с небольшого рабочего примера:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
import org.junit.Test import org.springframework.test.web.reactive.server.WebTestClient import org.springframework.web.reactive.function.server.ServerResponse.ok import org.springframework.web.reactive.function.server.router class AppRoutesTest { @Test fun testSimpleGet() { val routerFunction = router { GET( "/isokay" , { _ -> ok().build() }) } val client = WebTestClient.bindToRouterFunction(routerFunction).build() client.get() .uri( "/isokay" ) .exchange() .expectStatus().isOk } } |
Сердцем определения маршрута является функция «маршрутизатор»:
1
2
3
4
5
|
import org.springframework.web.reactive.function.server.router ... val routerFunction = router { GET( "/isokay" , { _ -> ok().build() }) } |
который определяется следующим образом:
1
|
fun router(routes: RouterFunctionDsl.() -> Unit) = RouterFunctionDsl().apply(routes).router() |
Параметр «маршруты» представляет собой специальный тип лямбда-выражения, называемый лямбда-выражением с получателем . Это означает, что в контексте функции маршрутизатора это лямбда-выражение может быть вызвано только экземплярами «RouterFunctionDsl», что и делается в теле функции с использованием метода apply , это также означает в теле лямбда-выражения « this »относится к экземпляру« RouterFunctionDsl ». Знание этого открывает доступ к методам RouterFunctionDsl, одним из которых является GET, который используется в примере, GET определяется следующим образом:
1
2
3
|
fun GET(pattern: String, f: (ServerRequest) -> Mono<ServerResponse>) { ... } |
Есть другие способы выразить ту же конечную точку:
1
|
GET( "/isokay2" )({ _ -> ok().build() }) |
реализовано в Kotlin очень умно, как:
1
2
3
4
5
|
fun GET(pattern: String): RequestPredicate = RequestPredicates.GET(pattern) operator fun RequestPredicate.invoke(f: (ServerRequest) -> Mono<ServerResponse>) { ... } |
Здесь GET с шаблоном возвращает «RequestPredicate», для которого была определена функция расширения (в контексте DSL), называемая invoke, которая, в свою очередь, является оператором со специальным именем .
Или третий способ:
1
|
"/isokay" { _ -> ok().build() } |
который реализуется путем добавления функции расширения для типа String и определяется следующим образом:
1
2
3
|
operator fun String.invoke(f: (ServerRequest) -> Mono<ServerResponse>) { ... } |
Мне кажется, что Spring Webflux превосходно использует Kotlin DSL для упрощения чтения некоторых из этих определений маршрутов, оставаясь при этом лаконичным.
Это должно обеспечить достаточно учебника для изучения исходного кода Routing DSL в Spring Webflux .
Мои образцы доступны в репозитории github здесь — https://github.com/bijukunjummen/webflux-route-with-kotlin
Ссылка: | Spring Webflux — Kotlin DSL — пошаговое руководство по внедрению от нашего партнера JCG Биджу Кунджуммена в блоге all and sundry. |