Update Kotlin documentation

This commit is contained in:
Sebastien Deleuze
2019-05-31 13:28:28 +02:00
parent 3794a57651
commit e0bb712bfc

View File

@@ -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 <<testing#testcontext-junit-jupiter-di#spring-web-reactive, dedicated section>>,
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]