diff --git a/src/docs/asciidoc/languages/kotlin.adoc b/src/docs/asciidoc/languages/kotlin.adoc index 2688fc3484..3721608b44 100644 --- a/src/docs/asciidoc/languages/kotlin.adoc +++ b/src/docs/asciidoc/languages/kotlin.adoc @@ -186,15 +186,28 @@ In Java, you can, for example, write the following: ---- ==== -In Kotlin, with reified type parameters and `GenericApplicationContext` -Kotlin extensions, you can instead write the following: +In Kotlin, with reified type parameters and `GenericApplicationContext` Kotlin extensions, +you can instead write the following: ==== [source,kotlin,indent=0] ---- val context = GenericApplicationContext().apply { registerBean() - registerBean { Bar(it.getBean()) } + registerBean { Bar(it.getBean()) } + } +---- +==== + +If the class `Bar` has a single constructor, you can even just specify the bean class, +the constructor parameters will be autowired by type: + +==== +[source,kotlin,indent=0] +---- + val context = GenericApplicationContext().apply { + registerBean() + registerBean() } ---- ==== @@ -208,39 +221,23 @@ how beans are registered. The following example creates a `Play` profile: ==== [source,kotlin,indent=0] ---- - fun beans() = beans { - bean() - bean() - bean("webHandler") { - RouterFunctions.toWebHandler( - ref().router(), - HandlerStrategies.builder().viewResolver(ref()).build() - ) - } - bean("messageSource") { - ReloadableResourceBundleMessageSource().apply { - setBasename("messages") - setDefaultEncoding("UTF-8") + val myBeans = beans { + bean() + bean() + bean("bazBean") { + Baz().apply { + message = "Hello world" } } - bean { - val prefix = "classpath:/templates/" - val suffix = ".mustache" - val loader = MustacheResourceTemplateLoader(prefix, suffix) - MustacheViewResolver(Mustache.compiler().withLoader(loader)).apply { - setPrefix(prefix) - setSuffix(suffix) - } - } - profile("play") { - bean() + profile("foobar") { + bean { FooBar(ref("bazBean")) } } } ---- ==== -In the preceding example, `bean()` uses autowiring by constructor, and `ref()` -is a shortcut for `applicationContext.getBean(Routes::class.java)`. +NOTE: This DSL is programmatic, meaning it allows custom registration logic of beans +through an `if` expression, a `for` loop, or any other Kotlin constructs. You can then use this `beans()` function to register beans on the application context, as the following example shows: @@ -249,19 +246,16 @@ as the following example shows: [source,kotlin,indent=0] ---- val context = GenericApplicationContext().apply { - beans().initialize(this) + myBeans.initialize(this) refresh() } ---- ==== -NOTE: This DSL is programmatic, meaning it allows custom registration logic of beans -through an `if` expression, a `for` loop, or any other Kotlin constructs. -See https://github.com/sdeleuze/spring-kotlin-functional/blob/master/src/main/kotlin/functional/Beans.kt[spring-kotlin-functional beans declaration] -for a concrete example. +See https://github.com/sdeleuze/spring-kotlin-functional[spring-kotlin-functional beans declaration] for a concrete example. -NOTE: Spring Boot is based on Java configuration and +NOTE: Spring Boot is based on JavaConfig and https://github.com/spring-projects/spring-boot/issues/8115[does not yet provide specific support for functional bean definition], but you can experimentally use functional bean definitions through Spring Boot's `ApplicationContextInitializer` support. See https://stackoverflow.com/questions/45935931/how-to-use-functional-bean-definition-kotlin-dsl-with-spring-boot-and-spring-w/46033685#46033685[this Stack Overflow answer] @@ -278,8 +272,8 @@ for more details and up-to-date information. Spring Framework now comes with a {doc-root}/spring-framework/docs/{spring-version}/kdoc-api/spring-framework/org.springframework.web.reactive.function.server/-router-function-dsl/[Kotlin routing DSL] -that lets you use the <> to write clean and idiomatic Kotlin code, as the following example shows: +that lets you use the <> to write clean and idiomatic Kotlin code, +as the following example shows: ==== [source,kotlin,indent=0] @@ -307,7 +301,7 @@ NOTE: This DSL is programmatic, meaning that it allows custom registration logic through an `if` expression, a `for` loop, or any other Kotlin constructs. That can be useful when you need to register routes depending on dynamic data (for example, from a database). -See https://github.com/mixitconf/mixit/tree/bad6b92bce6193f9b3f696af9d416c276501dbf1/src/main/kotlin/mixit/web/routes[MiXiT project routes] +See https://github.com/mixitconf/mixit/tree/dafd5ccc92dfab6d9c306fcb60b28921a1ccbf79/src/main/kotlin/mixit/web/routes[MiXiT project routes] for a concrete example. @@ -583,9 +577,43 @@ all HTTP methods will be matched, not only the `GET` one. === Testing -This section address testing with the combination of Kotlin and the Spring Framework. +This section addresses testing with the combination of Kotlin and Spring Framework. The recommended testing framework +is https://junit.org/junit5/[JUnit 5], as well as https://mockk.io/[Mockk] for mocking. +==== Constructor injection + +As described in the <>, JUnit 5 allows +constructor injection of beans which is pretty useful with Kotlin in order to use `val` instead of `lateinit var `. + + +==== +[source] +---- +@SpringJUnitConfig(TestConfig::class) +class OrderServiceIntegrationTests(@Autowired val orderService: OrderService, + @Autowired val customerService: CustomerService) { + + // tests that use the injected OrderService and CustomerService +} +---- +==== + +You can also use `@Autowired` at constructor level to autowire all parameters. + +==== +[source] +---- +@SpringJUnitConfig(TestConfig::class) +class OrderServiceIntegrationTests @Autowired constructor( + val orderService: OrderService, + val customerService: CustomerService) { + + // tests that use the injected OrderService and CustomerService +} +---- +==== + ==== `PER_CLASS` Lifecycle @@ -681,20 +709,15 @@ See also the related https://jira.spring.io/browse/SPR-16057[SPR-16057] issue. [[kotlin-getting-started]] == Getting Started -This section describes the fastest way to get started with a project that combines -Kotlin and the Spring Framework. +The easiest way to learn how to build a Spring application with Kotlin is to follow +https://spring.io/guides/tutorials/spring-boot-kotlin/[the dedicated tutorial]. - -=== Using `start.spring.io` +=== `start.spring.io` The easiest way to start a new Spring Framework 5 project in Kotlin is to create a new Spring Boot 2 project on https://start.spring.io/#!language=kotlin[start.spring.io]. -You can also create a standalone WebFlux project, as described in -https://spring.io/blog/2017/08/01/spring-framework-5-kotlin-apis-the-functional-way[this blog post]. - - === Choosing the Web Flavor @@ -702,11 +725,11 @@ Spring Framework now comes with two different web stacks: <> <>. Spring WebFlux is recommended if you want to create applications that will deal with latency, -long-lived connections, o streaming scenarios or if you want to use the web functional +long-lived connections, streaming scenarios or if you want to use the web functional Kotlin DSL. For other use cases, especially if you are using blocking technologies such as JPA, Spring -MVC and its annotation-based programming model is a perfectly valid and fully supported choice. +MVC and its annotation-based programming model is the recommended choice. @@ -724,27 +747,6 @@ Kotlin and the Spring Framework: * https://kotlin.link/[Awesome Kotlin] - -=== Tutorials - -We recommend the following tutorials: - -* https://spring.io/guides/tutorials/spring-boot-kotlin/[Building web applications with Spring Boot and Kotlin] -* https://kotlinlang.org/docs/tutorials/spring-boot-restful.html[Creating a RESTful Web Service with Spring Boot] - - - -=== Blog posts - -The following blog posts provide further details: - -* https://spring.io/blog/2016/02/15/developing-spring-boot-applications-with-kotlin[Developing Spring Boot applications with Kotlin] -* https://spring.io/blog/2016/03/20/a-geospatial-messenger-with-kotlin-spring-boot-and-postgresql[A Geospatial Messenger with Kotlin, Spring Boot and PostgreSQL] -* https://spring.io/blog/2017/01/04/introducing-kotlin-support-in-spring-framework-5-0[Introducing Kotlin support in Spring Framework 5.0] -* https://spring.io/blog/2017/08/01/spring-framework-5-kotlin-apis-the-functional-way[Spring Framework 5 Kotlin APIs, the functional way] - - - === Examples The following Github projects offer examples that you can learn from and possibly even extend: @@ -764,22 +766,19 @@ The following Github projects offer examples that you can learn from and possibl The following list categorizes the pending issues related to Spring and Kotlin support: * Spring Framework -** https://jira.spring.io/browse/SPR-16057[Unable to use WebTestClient with mock server in Kotlin] -** https://jira.spring.io/browse/SPR-15942[Support null-safety at generics, varargs and array elements level] -** https://jira.spring.io/browse/SPR-15413[Add support for Kotlin coroutines] +** https://github.com/spring-projects/spring-framework/issues/20606[Unable to use WebTestClient with mock server in Kotlin] +** https://github.com/spring-projects/spring-framework/issues/20496[Support null-safety at generics, varargs and array elements level] +** https://github.com/spring-projects/spring-framework/issues/19975[Add support for Kotlin coroutines] * Spring Boot ** https://github.com/spring-projects/spring-boot/issues/8762[Allow `@ConfigurationProperties` binding for immutable POJOs] -** https://github.com/spring-projects/spring-boot/issues/1254[Allow `@ConfigurationProperties` binding on interfaces] ** https://github.com/spring-projects/spring-boot/issues/8115[Expose the functional bean registration API via `SpringApplication`] ** https://github.com/spring-projects/spring-boot/issues/10712[Add null-safety annotations on Spring Boot APIs] ** https://github.com/spring-projects/spring-boot/issues/9486[Use Kotlin's bom to provide dependency management for Kotlin] * Kotlin ** https://youtrack.jetbrains.com/issue/KT-6380[Parent issue for Spring Framework support] ** https://youtrack.jetbrains.com/issue/KT-5464[Kotlin requires type inference where Java doesn't] -** https://github.com/Kotlin/KEEP/issues/79[Better generics null-safety support] ** https://youtrack.jetbrains.com/issue/KT-20283[Smart cast regression with open classes] ** https://youtrack.jetbrains.com/issue/KT-14984[Impossible to pass not all SAM argument as function] -** https://youtrack.jetbrains.com/issue/KT-19592[Apply JSR 305 meta-annotations to generic type parameters] -** https://youtrack.jetbrains.com/issue/KT-18398[Provide a way for libraries to avoid mixing Kotlin 1.0 and 1.1 dependencies] ** https://youtrack.jetbrains.com/issue/KT-15125[Support JSR 223 bindings directly via script variables] -** https://youtrack.jetbrains.com/issue/KT-15467[Support all-open and no-arg compiler plugins in Kotlin Eclipse plugin] +** https://youtrack.jetbrains.com/issue/KT-6653[Kotlin properties do not override Java-style getters and setters] +