Update Kotlin documentation
This commit is contained in:
@@ -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]
|
||||
|
||||
Reference in New Issue
Block a user