diff --git a/src/docs/asciidoc/languages/kotlin.adoc b/src/docs/asciidoc/languages/kotlin.adoc index 4260736018..47468da686 100644 --- a/src/docs/asciidoc/languages/kotlin.adoc +++ b/src/docs/asciidoc/languages/kotlin.adoc @@ -2,35 +2,31 @@ = Kotlin https://kotlinlang.org[Kotlin] is a statically typed language that targets the JVM -(and other platforms), which allows writing concise and elegant code while providing +(and other platforms) which allows writing concise and elegant code while providing very good https://kotlinlang.org/docs/reference/java-interop.html[interoperability] with existing libraries written in Java. -The Spring Framework provides first-class support for Kotlin that lets developers write -Kotlin applications almost as if the Spring Framework were a native Kotlin framework. +The Spring Framework provides first-class support for Kotlin and lets developers write +Kotlin applications almost as if the Spring Framework was a native Kotlin framework. + +The easiest way to build a Spring application with Kotlin is to leverage Spring Boot and +its https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-kotlin.html[dedicated Kotlin support]. +https://spring.io/guides/tutorials/spring-boot-kotlin/[This comprehensive tutorial] +will teach you how to build Spring Boot applications with Kotlin using https://start.spring.io/#!language=kotlin&type=gradle-project[start.spring.io]. -The easiest way to learn about Spring and Kotlin is to follow -https://spring.io/guides/tutorials/spring-boot-kotlin/[this comprehensive tutorial]. Feel free to join the #spring channel of https://slack.kotlinlang.org/[Kotlin Slack] or ask a question with `spring` and `kotlin` as tags on https://stackoverflow.com/questions/tagged/spring+kotlin[Stackoverflow] if you need support. - - - [[kotlin-requirements]] == Requirements -Spring Framework 5.2 supports Kotlin 1.3+ and requires +Spring Framework supports Kotlin 1.3 and requires https://bintray.com/bintray/jcenter/org.jetbrains.kotlin%3Akotlin-stdlib[`kotlin-stdlib`] -(or one of its variants, such as https://bintray.com/bintray/jcenter/org.jetbrains.kotlin%3Akotlin-stdlib-jre8[`kotlin-stdlib-jre8`] -or https://bintray.com/bintray/jcenter/org.jetbrains.kotlin%3Akotlin-stdlib-jdk8[`kotlin-stdlib-jdk8`]) +(or one of its variants, such as https://bintray.com/bintray/jcenter/org.jetbrains.kotlin%3Akotlin-stdlib-jdk8[`kotlin-stdlib-jdk8`]) and https://bintray.com/bintray/jcenter/org.jetbrains.kotlin%3Akotlin-reflect[`kotlin-reflect`] to be present on the classpath. They are provided by default if you bootstrap a Kotlin project on -https://start.spring.io/#!language=kotlin[start.spring.io]. - - - +https://start.spring.io/#!language=kotlin&type=gradle-project[start.spring.io]. [[kotlin-extensions]] == Extensions @@ -75,9 +71,6 @@ With Kotlin and the Spring Framework extensions, you can instead write the follo As in Java, `users` in Kotlin is strongly typed, but Kotlin's clever type inference allows for shorter syntax. - - - [[kotlin-null-safety]] == Null-safety @@ -113,9 +106,6 @@ NOTE: Generic type arguments, varargs, and array elements nullability are not su but should be in an upcoming release. See https://github.com/Kotlin/KEEP/issues/79[this discussion] for up-to-date information. - - - [[kotlin-classes-interfaces]] == Classes and Interfaces @@ -136,9 +126,6 @@ You can declare configuration classes as https://kotlinlang.org/docs/reference/nested-classes.html[top level or nested but not inner], since the later requires a reference to the outer class. - - - [[kotlin-annotations]] == Annotations @@ -165,13 +152,10 @@ https://kotlinlang.org/docs/reference/annotations.html#annotation-use-site-targe such as `@field:NotNull` or `@get:Size(min=5, max=15)`, as described in https://stackoverflow.com/a/35853200/1092077[this Stack Overflow response]. - - - [[kotlin-bean-definition-dsl]] == Bean Definition DSL -Spring Framework 5 introduces a new way to register beans in a functional way by using lambdas +Spring Framework supports registering beans in a functional way by using lambdas as an alternative to XML or Java configuration (`@Configuration` and `@Bean`). In a nutshell, it lets you register beans with a lambda that acts as a `FactoryBean`. This mechanism is very efficient, as it does not require any reflection or CGLIB proxies. @@ -276,21 +260,15 @@ as the following example shows: ---- -See https://github.com/sdeleuze/spring-kotlin-functional[spring-kotlin-functional beans declaration] for a concrete example. - 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] -for more details and up-to-date information. - - - +for more details and up-to-date information. See also the experimental Kofu DSL developed in https://github.com/spring-projects/spring-fu[Spring Fu incubator]. [[kotlin-web]] == Web - === Router DSL Spring Framework comes with a Kotlin router DSL available in 3 flavors: @@ -332,7 +310,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. - === MockMvc DSL A Kotlin DSL is provided via `MockMvc` Kotlin extensions in order to provide a more @@ -358,8 +335,6 @@ mockMvc.get("/person/{name}", "Lee") { } ---- - - === Kotlin Script Templates As of version 4.3, Spring Framework provides a @@ -440,8 +415,8 @@ https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coro * Collect operations are suspending functions * https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/map.html[`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. +Read this blog post about https://spring.io/blog/2019/04/12/going-reactive-with-spring-coroutines-and-kotlin-flow[Going Reactive with Spring, Coroutines and Kotlin Flow] +for more details, including how to run code concurrently with Coroutines. === Controllers @@ -572,8 +547,6 @@ class UserHandler(builder: WebClient.Builder) { } ---- - - [[kotlin-spring-projects-in-kotlin]] == Spring Projects in Kotlin @@ -590,11 +563,14 @@ class. This also applies to member functions, in that they need to be marked as While Kotlin's JVM-friendly design is generally frictionless with Spring, this specific Kotlin feature can prevent the application from starting, if this fact is not taken into consideration. This is because -Spring beans (such as `@Configuration` classes which need to be inherited at runtime for technical +Spring beans (such as `@Configuration` annotated classes which by default need to be inherited at runtime for technical reasons) are normally proxied by CGLIB. The workaround was to add an `open` keyword on each class and -member function of Spring beans that are proxied by CGLIB (such as `@Configuration` classes), which can +member function of Spring beans that are proxied by CGLIB, which can quickly become painful and is against the Kotlin principle of keeping code concise and predictable. +NOTE: It is also possible to avoid CGLIB proxies on configurations by using `@Configuration(proxyBeanMethods = false)`, +see {api-spring-framework}/context/annotation/Configuration.html#proxyBeanMethods--[`proxyBeanMethods` Javadoc] for more details. + Fortunately, Kotlin now provides a https://kotlinlang.org/docs/reference/compiler-plugins.html#kotlin-spring-compiler-plugin[`kotlin-spring`] plugin (a preconfigured version of the `kotlin-allopen` plugin) that automatically opens classes @@ -610,11 +586,9 @@ Meta-annotations support means that types annotated with `@Configuration`, `@Con `@RestController`, `@Service`, or `@Repository` are automatically opened since these annotations are meta-annotated with `@Component`. -https://start.spring.io/#!language=kotlin[start.spring.io] enables it by default, so, in practice, +https://start.spring.io/#!language=kotlin&type=gradle-project[start.spring.io] enables it by default, so, in practice, you can write your Kotlin beans without any additional `open` keyword, as in Java. - - === Using Immutable Class Instances for Persistence In Kotlin, it is convenient and considered to be a best practice to declare read-only properties @@ -658,8 +632,6 @@ NOTE: As of the Kay release train, Spring Data supports Kotlin immutable class i does not require the `kotlin-noarg` plugin if the module uses Spring Data object mappings (such as MongoDB, Redis, Cassandra, and others). - - === Injecting Dependencies Our recommendation is to try and favor constructor injection with `val` read-only (and @@ -675,9 +647,9 @@ as the following example shows: ) ---- -NOTE: As of Spring Framework 4.3, classes with a single constructor have their parameters -automatically autowired, that's why there is no need for an explicit `@Autowired constructor` -in the example shown above. +NOTE: Classes with a single constructor have their parameters automatically autowired, +that's why there is no need for an explicit `@Autowired constructor` in the example shown +above. If you really need to use field injection, you can use the `lateinit var` construct, as the following example shows: @@ -695,8 +667,6 @@ as the following example shows: } ---- - - === Injecting Configuration Properties In Java, you can inject configuration properties by using annotations (such as `@Value("${property}")`). @@ -706,6 +676,10 @@ https://kotlinlang.org/docs/reference/idioms.html#string-interpolation[string in Therefore, if you wish to use the `@Value` annotation in Kotlin, you need to escape the `$` character by writing `@Value("\${property}")`. +NOTE: If you use Spring Boot, you should probably use +https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-typesafe-configuration-properties[`@ConfigurationProperties`] +instead of `@Value` annotations. + As an alternative, you can customize the properties placeholder prefix by declaring the following configuration beans: @@ -732,16 +706,6 @@ that uses the `${...}` syntax, with configuration beans, as the following exampl fun defaultPropertyConfigurer() = PropertySourcesPlaceholderConfigurer() ---- -NOTE: If you use Spring Boot, you can use -https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-typesafe-configuration-properties[`@ConfigurationProperties`] -instead of `@Value` annotations. However, currently, this only works with `lateinit` or -nullable `var` properties (we recommended the former), since immutable classes initialized -by constructors are not yet supported. See these issues about -https://github.com/spring-projects/spring-boot/issues/8762[`@ConfigurationProperties` binding for immutable POJOs] -and https://github.com/spring-projects/spring-boot/issues/1254[`@ConfigurationProperties` binding on interfaces] -for more details. - - === Checked Exceptions @@ -755,8 +719,6 @@ To get the original exception thrown like in Java, methods should be annotated w https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.jvm/-throws/index.html[`@Throws`] to specify explicitly the checked exceptions thrown (for example `@Throws(IOException::class)`). - - === Annotation Array Attributes Kotlin annotations are mostly similar to Java annotations, but array attributes (which are @@ -801,50 +763,33 @@ use a shortcut annotation, such as `@GetMapping`, `@PostMapping`, and others. NOTE: Reminder: If the `@RequestMapping` `method` attribute is not specified, all HTTP methods will be matched, not only the `GET` one. - - === Testing 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`. - +in order to use `val` instead of `lateinit var`. You can use +{api-spring-framework}/test/context/TestConstructor.html[`@TestConstructor(autowire = true)`] +to enable autowiring for all parameters. ==== -[source] +[source,kotlin,indent=0] ---- @SpringJUnitConfig(TestConfig::class) -class OrderServiceIntegrationTests(@Autowired val orderService: OrderService, - @Autowired val customerService: CustomerService) { +@TestConstructor(autowire = true) +class OrderServiceIntegrationTests(val orderService: OrderService, + 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 Kotlin lets you specify meaningful test function names between backticks (```). @@ -857,8 +802,9 @@ file with a `junit.jupiter.testinstance.lifecycle.default = per_class` property. The following example demonstrates `@BeforeAll` and `@AfterAll` annotations on non-static methods: -[source] +[source,kotlin,indent=0] ---- +@TestInstance(TestInstance.Lifecycle.PER_CLASS) class IntegrationTests { val application = Application(8181) @@ -887,13 +833,12 @@ class IntegrationTests { } ---- - ==== Specification-like Tests You can create specification-like tests with JUnit 5 and Kotlin. The following example shows how to do so: -[source] +[source,kotlin,indent=0] ---- class SpecificationLikeTests { @@ -917,7 +862,6 @@ class SpecificationLikeTests { } ---- - [[kotlin-webtestclient-issue]] ==== `WebTestClient` Type Inference Issue in Kotlin @@ -927,23 +871,16 @@ since it provides a workaround for the Kotlin issue with the Java API. See also the related https://jira.spring.io/browse/SPR-16057[SPR-16057] issue. - - - [[kotlin-getting-started]] == Getting Started 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]. - - === `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]. - - +The easiest way to start a new Spring Framework project in Kotlin is to create a new Spring +Boot 2 project on https://start.spring.io/#!language=kotlin&type=gradle-project[start.spring.io]. === Choosing the Web Flavor @@ -957,9 +894,6 @@ 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 the recommended choice. - - - [[kotlin-resources]] == Resources @@ -973,8 +907,6 @@ Kotlin and the Spring Framework: * https://blog.jetbrains.com/kotlin/[Kotlin blog] * https://kotlin.link/[Awesome Kotlin] - - === Examples The following Github projects offer examples that you can learn from and possibly even extend: @@ -987,8 +919,6 @@ The following Github projects offer examples that you can learn from and possibl * https://github.com/sdeleuze/spring-kotlin-deepdive[spring-kotlin-deepdive]: A step-by-step migration guide for Boot 1.0 and Java to Boot 2.0 and Kotlin * https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-kotlin-samples/spring-cloud-gcp-kotlin-app-sample[spring-cloud-gcp-kotlin-app-sample]: Spring Boot with Google Cloud Platform Integrations - - === Issues The following list categorizes the pending issues related to Spring and Kotlin support: @@ -996,12 +926,6 @@ The following list categorizes the pending issues related to Spring and Kotlin s * Spring Framework ** 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/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]