647 lines
25 KiB
Plaintext
647 lines
25 KiB
Plaintext
[[kotlin]]
|
||
= Kotlin support
|
||
:doc-root: https://docs.spring.io
|
||
:api-spring-framework: {doc-root}/spring-framework/docs/{spring-version}/javadoc-api/org/springframework
|
||
:toc: left
|
||
:toclevels: 2
|
||
:docinfo1:
|
||
|
||
[[introduction]]
|
||
== Introduction
|
||
|
||
https://kotlinlang.org[Kotlin] is a statically-typed language targeting the JVM (and other platforms)
|
||
which allows writing concise and elegant code while providing a very good
|
||
https://kotlinlang.org/docs/reference/java-interop.html[interoperability] with
|
||
existing libraries written in Java.
|
||
|
||
Spring Framework 5 introduces first-class support for Kotlin and allows developers to write
|
||
Spring + Kotlin applications almost as if the Spring Framework was a native Kotlin framework.
|
||
|
||
[[requirements]]
|
||
== Requirements ==
|
||
|
||
Spring Framework supports Kotlin 1.1+ and requires
|
||
https://bintray.com/bintray/jcenter/org.jetbrains.kotlin%3Akotlin-stdlib[`kotlin-stdlib`]
|
||
(or one of its https://bintray.com/bintray/jcenter/org.jetbrains.kotlin%3Akotlin-stdlib-jre7[`kotlin-stdlib-jre7`]
|
||
/ https://bintray.com/bintray/jcenter/org.jetbrains.kotlin%3Akotlin-stdlib-jre8[`kotlin-stdlib-jre8`] variants)
|
||
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 one bootstraps a Kotlin project on
|
||
https://start.spring.io/#!language=kotlin[start.spring.io].
|
||
|
||
[[extensions]]
|
||
== Extensions
|
||
|
||
Thanks to its great https://kotlinlang.org/docs/reference/java-interop.html[Java interoperability]
|
||
and to https://kotlinlang.org/docs/reference/extensions.html[Kotlin extensions], Spring
|
||
Framework Kotlin APIs leverage regular Java APIs and are additionally enhanced by a few Kotlin specific APIs
|
||
available out of the box within Spring Framework 5 artifacts.
|
||
|
||
{doc-root}/spring-framework/docs/{spring-version}/kdoc-api/spring-framework/[Spring Framework KDoc API] lists
|
||
and documents all the Kotlin extensions and DSLs available.
|
||
|
||
[NOTE]
|
||
====
|
||
Keep in mind that Kotlin extensions need to be imported to be used. This means
|
||
for example that the `GenericApplicationContext.registerBean` Kotlin extension
|
||
will only be available if `import org.springframework.context.support.registerBean` is imported.
|
||
That said, similar to static imports, an IDE should automatically suggest the import in most cases.
|
||
====
|
||
|
||
For example, https://kotlinlang.org/docs/reference/inline-functions.html#reified-type-parameters[Kotlin reified type parameters]
|
||
provide a workaround for JVM https://docs.oracle.com/javase/tutorial/java/generics/erasure.html[generics type erasure],
|
||
and Spring Framework provides some extensions to take advantage of this feature.
|
||
This allows for a better Kotlin API `RestTemplate`, the new `WebClient` from Spring
|
||
WebFlux and for various other APIs.
|
||
|
||
[NOTE]
|
||
====
|
||
Other libraries like Reactor and Spring Data also provide Kotlin extensions
|
||
for their APIs, thus giving a better Kotlin development experience overall.
|
||
====
|
||
|
||
To retrieve a list of `Foo` objects in Java, one would normally write:
|
||
|
||
[source,java]
|
||
----
|
||
Flux<User> users = client.get().retrieve().bodyToFlux(User.class)
|
||
----
|
||
|
||
Whilst with Kotlin and Spring Framework extensions, one is able to write:
|
||
|
||
[source,kotlin]
|
||
----
|
||
val users = client.get().retrieve().bodyToFlux<User>()
|
||
// or (both are equivalent)
|
||
val users : Flux<User> = client.get().retrieve().bodyToFlux()
|
||
----
|
||
|
||
As in Java, `users` in Kotlin is strongly typed, but Kotlin's clever type inference allows
|
||
for a shorter syntax.
|
||
|
||
[[null-safety]]
|
||
== Null-safety
|
||
|
||
One of Kotlin's key features is https://kotlinlang.org/docs/reference/null-safety.html[null-safety]
|
||
which cleanly deals with `null` values at compile time rather than bumping into the famous
|
||
`NullPointerException` at runtime. This makes applications safer through nullability
|
||
declarations and expressing "value or no value" semantics without paying the cost of wrappers like `Optional`.
|
||
(Kotlin allows using functional constructs with nullable values; check out this
|
||
http://www.baeldung.com/kotlin-null-safety[comprehensive guide to Kotlin null-safety].)
|
||
|
||
Although Java does not allow one to express null-safety in its type-system, Spring Framework now
|
||
provides <<core#null-safety,null-safety of the whole Spring Framework API>>
|
||
via tooling-friendly annotations declared in the `org.springframework.lang` package.
|
||
By default, types from Java APIs used in Kotlin are recognized as
|
||
https://kotlinlang.org/docs/reference/java-interop.html#null-safety-and-platform-types[platform types]
|
||
for which null-checks are relaxed.
|
||
https://github.com/Kotlin/KEEP/blob/jsr-305/proposals/jsr-305-custom-nullability-qualifiers.md[Kotlin support for JSR 305 annotations]
|
||
+ Spring nullability annotations provide null-safety for the whole Spring Framework API to Kotlin developers,
|
||
with the advantage of dealing with `null` related issues at compile time.
|
||
|
||
[NOTE]
|
||
====
|
||
Libraries like Reactor or Spring Data provide null-safe APIs leveraging this feature.
|
||
====
|
||
|
||
The JSR 305 checks can be configured by adding the `-Xjsr305` compiler flag with the following
|
||
options: `-Xjsr305={strict|warn|ignore}`.
|
||
|
||
For kotlin versions 1.1.50+, the default behavior is the same to `-Xjsr305=warn`. The
|
||
`strict` value should be considered experimental (Spring API nullability declaration could
|
||
evolve even between minor releases and more checks may be added in the future).
|
||
|
||
[NOTE]
|
||
====
|
||
Generic type arguments, varargs and array elements nullability are not supported yet,
|
||
but should be in an upcoming release, see https://jira.spring.io/browse/SPR-15942[SPR-15942]
|
||
for up-to-date information.
|
||
====
|
||
|
||
[[classes-interfaces]]
|
||
== Classes & Interfaces
|
||
|
||
Spring Framework supports various Kotlin constructs like instantiating Kotlin classes
|
||
via primary constructors, immutable classes data binding and function optional parameters
|
||
with default values.
|
||
|
||
Kotlin parameter names are recognized via a dedicated `KotlinReflectionParameterNameDiscoverer`
|
||
which allows finding interface method parameter names without requiring the Java 8 `-parameters`
|
||
compiler flag enabled during compilation.
|
||
|
||
https://github.com/FasterXML/jackson-module-kotlin[Jackson Kotlin module] which is required
|
||
for serializing / deserializing JSON data is automatically registered when
|
||
found in the classpath and a warning message will be logged if Jackson and Kotlin are
|
||
detected without the Jackson Kotlin module present.
|
||
|
||
[NOTE]
|
||
====
|
||
As of Spring Boot 2.0, Jackson Kotlin module is automatically provided via the JSON starter.
|
||
====
|
||
|
||
[[annotations]]
|
||
== Annotations
|
||
|
||
Spring Framework also takes advantage of https://kotlinlang.org/docs/reference/null-safety.html[Kotlin null-safety]
|
||
to determine if a HTTP parameter is required without having to explicitly
|
||
define the `required` attribute. That means `@RequestParam name: String?` will be treated
|
||
as not required and conversely `@RequestParam name: String` as being required.
|
||
This feature is also supported on the Spring Messaging `@Header` annotation.
|
||
|
||
In a similar fashion, Spring bean injection with `@Autowired` or `@Inject` uses this information
|
||
to determine if a bean is required or not. `@Autowired lateinit var foo: Foo` implies that a bean
|
||
of type `Foo` must be registered in the application context while `@Autowired lateinit var foo: Foo?`
|
||
won’t raise an error if such bean does not exist.
|
||
|
||
[[bean-definition-dsl]]
|
||
== Bean definition DSL
|
||
|
||
Spring Framework 5 introduces a new way to register beans in a functional way using lambdas
|
||
as an alternative to XML or JavaConfig (`@Configuration` and `@Bean`). In a nutshell,
|
||
it makes it possible to 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.
|
||
|
||
In Java, one may for example write:
|
||
|
||
[source,java]
|
||
----
|
||
GenericApplicationContext context = new GenericApplicationContext();
|
||
context.registerBean(Foo.class);
|
||
context.registerBean(Bar.class, () -> new
|
||
Bar(context.getBean(Foo.class))
|
||
);
|
||
----
|
||
|
||
Whilst in Kotlin with reified type parameters and `GenericApplicationContext`
|
||
Kotlin extensions one can instead simply write:
|
||
|
||
[source,kotlin]
|
||
----
|
||
val context = GenericApplicationContext().apply {
|
||
registerBean<Foo>()
|
||
registerBean { Bar(it.getBean<Foo>()) }
|
||
}
|
||
----
|
||
|
||
In order to allow a more declarative approach and cleaner syntax, Spring Framework provides
|
||
a {doc-root}/spring-framework/docs/{spring-version}/kdoc-api/spring-framework/org.springframework.context.support/-bean-definition-dsl/[Kotlin bean definition DSL]
|
||
It declares an `ApplicationContextInitializer` via a clean declarative API
|
||
which enables one to deal with profiles and `Environment` for customizing
|
||
how beans are registered.
|
||
|
||
[source,kotlin]
|
||
----
|
||
fun beans() = beans {
|
||
bean<UserHandler>()
|
||
bean {
|
||
Routes(ref(), ref())
|
||
}
|
||
bean<WebHandler>("webHandler") {
|
||
RouterFunctions.toWebHandler(
|
||
ref<Routes>().router(),
|
||
HandlerStrategies.builder().viewResolver(ref()).build()
|
||
)
|
||
}
|
||
bean("messageSource") {
|
||
ReloadableResourceBundleMessageSource().apply {
|
||
setBasename("messages")
|
||
setDefaultEncoding("UTF-8")
|
||
}
|
||
}
|
||
bean {
|
||
val prefix = "classpath:/templates/"
|
||
val suffix = ".mustache"
|
||
val loader = MustacheResourceTemplateLoader(prefix, suffix)
|
||
MustacheViewResolver(Mustache.compiler().withLoader(loader)).apply {
|
||
setPrefix(prefix)
|
||
setSuffix(suffix)
|
||
}
|
||
}
|
||
profile("foo") {
|
||
bean<Foo>()
|
||
}
|
||
}
|
||
----
|
||
|
||
In this example, `Routes(ref(), ref())` is the equivalent of `Routes(ref<UserHandler>(), ref<MessageSource>())`
|
||
(types are not required thanks to Kotlin type inference) where `ref<UserHandler>()`
|
||
is a shortcut for `applicationContext.getBean(UserHandler::class.java)`.
|
||
|
||
This `beans()` function can then be used to register beans on the application context.
|
||
|
||
[source,kotlin]
|
||
----
|
||
val context = GenericApplicationContext().apply {
|
||
beans().invoke(this)
|
||
refresh()
|
||
}
|
||
----
|
||
|
||
[NOTE]
|
||
====
|
||
This DSL is programmatic, thus it allows custom registration logic of beans
|
||
via an `if` expression, a `for` loop or any other Kotlin constructs.
|
||
====
|
||
|
||
See https://github.com/sdeleuze/spring-kotlin-functional/blob/3d12ab102c28f4761bd6a0736e2f585713eb2243/src/main/kotlin/functional/Beans.kt[spring-kotlin-functional beans declaration]
|
||
for a concrete example.
|
||
|
||
[NOTE]
|
||
====
|
||
Spring Boot is based on Java Config and
|
||
https://github.com/spring-projects/spring-boot/issues/8115[does not provide specific support for functional bean definition yet],
|
||
but one can experimentally use functional bean definitions via 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.
|
||
====
|
||
|
||
[[web]]
|
||
== Web
|
||
|
||
=== WebFlux Functional DSL
|
||
|
||
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 allows one to leverage the <<reactive-web#webflux-fn,WebFlux functional
|
||
API>> for writing clean and idiomatic Kotlin code:
|
||
|
||
[source,kotlin]
|
||
----
|
||
router {
|
||
accept(TEXT_HTML).nest {
|
||
GET("/") { ok().render("index") }
|
||
GET("/sse") { ok().render("sse") }
|
||
GET("/users", userHandler::findAllView)
|
||
}
|
||
"/api".nest {
|
||
accept(APPLICATION_JSON).nest {
|
||
GET("/users", userHandler::findAll)
|
||
}
|
||
accept(TEXT_EVENT_STREAM).nest {
|
||
GET("/users", userHandler::stream)
|
||
}
|
||
}
|
||
resources("/**", ClassPathResource("static/"))
|
||
}
|
||
----
|
||
|
||
[NOTE]
|
||
====
|
||
This DSL is programmatic, thus it allows custom registration logic of beans
|
||
via an `if` expression, a `for` loop or any other Kotlin constructs. That can be useful when routes need to be registered
|
||
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]
|
||
for a concrete example.
|
||
|
||
=== Kotlin Script templates
|
||
|
||
As of version 4.3, Spring Framework provides a
|
||
http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/view/script/ScriptTemplateView.html[ScriptTemplateView]
|
||
to render templates using script engines that supports
|
||
https://www.jcp.org/en/jsr/detail?id=223[JSR-223].
|
||
Spring Framework 5 goes even further by extending this feature to WebFlux and supporting
|
||
https://jira.spring.io/browse/SPR-15064[i18n and nested templates].
|
||
|
||
Kotlin provides similar support and allows the rendering of Kotlin based templates, see
|
||
https://github.com/spring-projects/spring-framework/commit/badde3a479a53e1dd0777dd1bd5b55cb1021cf9e[this commit] for details.
|
||
|
||
This enables some interesting use cases like writing type-safe templates using
|
||
https://github.com/Kotlin/kotlinx.html[kotlinx.html] DSL or simply using Kotlin multiline `String` with interpolation.
|
||
|
||
This can allow one to write Kotlin templates with full autocompletion and
|
||
refactoring support in a supported IDE:
|
||
|
||
[source,kotlin]
|
||
----
|
||
import io.spring.demo.*
|
||
|
||
"""
|
||
${include("header")}
|
||
<h1>${i18n("title")}</h1>
|
||
<ul>
|
||
${users.joinToLine{ "<li>${i18n("user")} ${it.firstname} ${it.lastname}</li>" }}
|
||
</ul>
|
||
${include("footer")}
|
||
"""
|
||
----
|
||
|
||
See https://github.com/sdeleuze/kotlin-script-templating[kotlin-script-templating] example
|
||
project for more details.
|
||
|
||
[[spring-projects-in-kotlin]]
|
||
== Spring projects in Kotlin
|
||
|
||
This section provides a focus on some specific hints and recommendations worth
|
||
knowing when developing Spring projects in Kotlin.
|
||
|
||
=== Final by default
|
||
|
||
By default, https://discuss.kotlinlang.org/t/classes-final-by-default/166[all classes in Kotlin are `final`].
|
||
The `open` modifier on a class is the opposite of Java's `final`: it allows others to
|
||
inherit from this class. This also applies to member functions, in that they need to be marked as `open` to
|
||
be overridden.
|
||
|
||
Whilst 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 in
|
||
consideration. This is because Spring beans are normally proxified with CGLIB
|
||
- such as `@Configuration` classes - which need to be inherited at runtime for technical reasons.
|
||
|
||
Before Kotlin 1.0.6, one needed to add an `open` keyword on each class and member
|
||
functions of Spring beans proxified with CGLIB such as `@Configuration` classes.
|
||
|
||
Fortunately, Kotlin 1.0.6+ now provides a
|
||
https://kotlinlang.org/docs/reference/compiler-plugins.html#kotlin-spring-compiler-plugin[`kotlin-spring`]
|
||
plugin, a preconfigured version of `kotlin-allopen` plugin that automatically opens classes
|
||
and their member functions for types annotated or meta-annotated with one of the following
|
||
annotations:
|
||
|
||
* `@Component`
|
||
* `@Async`
|
||
* `@Transactional`
|
||
* `@Cacheable`
|
||
|
||
Meta-annotations support means that types annotated with `@Configuration`, `@Controller`,
|
||
`@RestController`, `@Service` or `@Repository` are automatically opened since these
|
||
annotations are meta-annotated with `@Component`.
|
||
|
||
http://start.spring.io/#!language=kotlin[start.spring.io] enables it by default.
|
||
|
||
=== Using immutable class instances for persistence
|
||
|
||
In Kotlin, it is very convenient and a best practice to declare read-only properties within
|
||
the primary constructor, as in the following example:
|
||
|
||
[source,kotlin]
|
||
----
|
||
class Person(val name: String, val age: Int)
|
||
----
|
||
|
||
But some persistence technologies like JPA require a default constructor, preventing this
|
||
kind of design. Fortunately, there is now a workaround for this
|
||
https://stackoverflow.com/questions/32038177/kotlin-with-jpa-default-constructor-hell["default constructor hell"]
|
||
since Kotlin provides a https://kotlinlang.org/docs/reference/compiler-plugins.html#kotlin-jpa-compiler-plugin[kotlin-jpa]
|
||
plugin which generates synthetic no-arg constructor for classes annotated with JPA annotations.
|
||
|
||
If you need to leverage this kind of mechanism for other persistence technologies, you can
|
||
configure https://kotlinlang.org/docs/reference/compiler-plugins.html#how-to-use-no-arg-plugin[kotlin-noarg]
|
||
plugin.
|
||
|
||
[NOTE]
|
||
====
|
||
As of Kay release train, Spring Data supports Kotlin immutable class instances
|
||
and should not require `kotlin-noarg` plugin if the module leverages Spring Data object
|
||
mapping (like with MongoDB, Redis, Cassandra, etc.).
|
||
====
|
||
|
||
=== Injecting dependencies
|
||
|
||
Our recommendation is to try and favor constructor injection with `val` read-only (and non-nullable when possible)
|
||
https://kotlinlang.org/docs/reference/properties.html[properties].
|
||
|
||
[source,kotlin]
|
||
----
|
||
@Component
|
||
class YourBean(
|
||
private val mongoTemplate: MongoTemplate,
|
||
private val solrClient: SolrClient
|
||
)
|
||
----
|
||
|
||
[NOTE]
|
||
====
|
||
As of Spring Framework 4.3, classes with a single constructor have its parameters
|
||
automatically autowired, that's why there is no need for `@Autowired constructor`
|
||
in the example shown above.
|
||
====
|
||
|
||
If one really needs to use field injection, use the `lateinit var` construct,
|
||
i.e.,
|
||
|
||
[source,kotlin]
|
||
----
|
||
@Component
|
||
class YourBean {
|
||
|
||
@Autowired
|
||
lateinit var mongoTemplate: MongoTemplate
|
||
|
||
@Autowired
|
||
lateinit var solrClient: SolrClient
|
||
}
|
||
----
|
||
|
||
=== Injecting configuration properties
|
||
|
||
In Java, one can inject configuration properties using annotations like `@Value("${property}")`,
|
||
however in Kotlin `$` is a reserved character that is used for https://kotlinlang.org/docs/reference/idioms.html#string-interpolation[string interpolation].
|
||
|
||
Therefore, if one wishes to use the `@Value` annotation in Kotlin, the `$`
|
||
character will need to be escaped by writing `@Value("\${property}")`.
|
||
|
||
As an alternative, it is possible to customize the properties placeholder prefix by declaring
|
||
the following configuration beans:
|
||
|
||
[source,kotlin]
|
||
----
|
||
@Bean
|
||
fun propertyConfigurer() = PropertySourcesPlaceholderConfigurer().apply {
|
||
setPlaceholderPrefix("%{")
|
||
}
|
||
----
|
||
|
||
Existing code (like Spring Boot actuators or `@LocalServerPort`) that
|
||
uses the `${...}` syntax, can be customised with configuration beans, like
|
||
this:
|
||
|
||
[source,kotlin]
|
||
----
|
||
@Bean
|
||
fun kotlinPropertyConfigurer() = PropertySourcesPlaceholderConfigurer().apply {
|
||
setPlaceholderPrefix("%{")
|
||
setIgnoreUnresolvablePlaceholders(true)
|
||
}
|
||
|
||
@Bean
|
||
fun defaultPropertyConfigurer() = PropertySourcesPlaceholderConfigurer()
|
||
----
|
||
|
||
[NOTE]
|
||
====
|
||
If Spring Boot is being used, then
|
||
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 can be used, but currently this only works with nullable `var`
|
||
properties (which is far from ideal) 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.
|
||
====
|
||
|
||
=== Annotation array attributes
|
||
|
||
Kotlin annotations are mostly similar to Java ones, but array attributes - which are
|
||
extensively used in Spring - behave differently. As explained in https://kotlinlang.org/docs/reference/annotations.html[Kotlin documentation]
|
||
unlike other attributes, the `value` attribute name can be omitted and when it is an array
|
||
attribute it is specified as a `vararg` parameter.
|
||
|
||
To understand what that means, let's take `@RequestMapping`, which is one
|
||
of the most widely used Spring annotations as an example. This Java annotation is declared as:
|
||
|
||
[source,java]
|
||
----
|
||
public @interface RequestMapping {
|
||
|
||
@AliasFor("path")
|
||
String[] value() default {};
|
||
|
||
@AliasFor("value")
|
||
String[] path() default {};
|
||
|
||
RequestMethod[] method() default {};
|
||
|
||
// ...
|
||
}
|
||
----
|
||
|
||
The typical use case for `@RequestMapping` is to map a handler method to a specific path
|
||
and method. In Java, it is possible to specify a single value for the
|
||
annotation array attribute and it will be automatically converted to an array.
|
||
|
||
That's why one can write
|
||
`@RequestMapping(value = "/foo", method = RequestMethod.GET)` or
|
||
`@RequestMapping(path = "/foo", method = RequestMethod.GET)`.
|
||
|
||
However, in Kotlin, one will have to write `@RequestMapping("/foo", method = arrayOf(RequestMethod.GET))`.
|
||
The variant using `path` is not recommended as it need to be written
|
||
`@RequestMapping(path = arrayOf("/foo"), method = arrayOf(RequestMethod.GET))`.
|
||
|
||
A workaround for this specific `method` attribute (the most common one) is to
|
||
use a shortcut annotation such as `@GetMapping` or `@PostMapping`, etc.
|
||
|
||
[NOTE]
|
||
====
|
||
Remininder: if the `@RequestMapping` `method` attribute is not specified, all HTTP methods will be matched,
|
||
not only the `GET` methods.
|
||
====
|
||
|
||
Improving the syntax and consistency of Kotlin annotation array attributes is discussed in
|
||
https://youtrack.jetbrains.com/issue/KT-11235[this Kotlin language design issue].
|
||
|
||
=== Testing
|
||
|
||
Kotlin allows one to specify meaningful test function names between backticks,
|
||
and as of JUnit 5 Kotlin test classes can use the `@TestInstance(TestInstance.Lifecycle.PER_CLASS)`
|
||
annotation to enable a single instantiation of test classes which allows the use of `@BeforeAll` and `@AfterAll`
|
||
annotations on non-static methods, which is a good fit for Kotlin.
|
||
|
||
It is now possible to change the default behavior to `PER_CLASS` thanks to a
|
||
`junit-platform.properties` file with a
|
||
`junit.jupiter.testinstance.lifecycle.default = per_class` property.
|
||
|
||
[source]
|
||
----
|
||
class IntegrationTests {
|
||
|
||
val application = Application(8181)
|
||
val client = WebClient.create("http://localhost:8181")
|
||
|
||
@BeforeAll
|
||
fun beforeAll() {
|
||
application.start()
|
||
}
|
||
|
||
@Test
|
||
fun `Find all users on HTML page`() {
|
||
client.get().uri("/users")
|
||
.accept(TEXT_HTML)
|
||
.retrieve()
|
||
.bodyToMono<String>()
|
||
.test()
|
||
.expectNextMatches { it.contains("Foo") }
|
||
.verifyComplete()
|
||
}
|
||
|
||
@AfterAll
|
||
fun afterAll() {
|
||
application.stop()
|
||
}
|
||
}
|
||
----
|
||
|
||
[[getting-started]]
|
||
== Getting started
|
||
|
||
=== 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].
|
||
|
||
It is also possible to 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
|
||
|
||
Spring Framework now comes with 2 different web stacks: <<web#mvc,Spring MVC>> and
|
||
<<reactive-web#spring-web-reactive,Spring WebFlux>>.
|
||
|
||
Spring WebFlux is recommended if one wants to create applications that will deal with latency,
|
||
long-lived connections, streaming scenarios or simply if one wants to use the web functional
|
||
Kotlin DSL.
|
||
|
||
For other use cases, especially if you are using blocking technologies like JPA, Spring
|
||
MVC and its annotation-based programming model is a perfectly valid and fully supported choice.
|
||
|
||
[[resources-started]]
|
||
== Resources
|
||
|
||
* http://kotlinlang.org/docs/reference/[Kotlin language reference]
|
||
* http://slack.kotlinlang.org/[Kotlin Slack] (with a dedicated #spring channel)
|
||
* https://try.kotlinlang.org/[Try Kotlin in your browser]
|
||
* https://blog.jetbrains.com/kotlin/[Kotlin blog]
|
||
* https://kotlin.link/[Awesome Kotlin]
|
||
|
||
=== Blog posts
|
||
|
||
* 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
|
||
|
||
* https://github.com/sdeleuze/spring-boot-kotlin-demo[spring-boot-kotlin-demo]: regular Spring Boot + Spring Data JPA project
|
||
* https://github.com/mixitconf/mixit[mixit]: Spring Boot 2 + WebFlux + Reactive Spring Data MongoDB
|
||
* https://github.com/sdeleuze/spring-kotlin-functional[spring-kotlin-functional]: standalone WebFlux + functional bean definition DSL
|
||
|
||
=== Tutorials
|
||
|
||
* https://kotlinlang.org/docs/tutorials/spring-boot-restful.html[Creating a RESTful Web Service with Spring Boot]
|
||
|
||
=== Issues
|
||
|
||
Here is a list of pending issues related to Spring + Kotlin support.
|
||
|
||
==== Spring Framework
|
||
|
||
* https://jira.spring.io/browse/SPR-15413[Add support for Kotlin coroutines]
|
||
|
||
==== Spring Boot
|
||
|
||
* https://github.com/spring-projects/spring-boot/issues/5537[Improve Kotlin support]
|
||
* 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/8511[Provide support for Kotlin KClass parameter in `SpringApplication.run()`]
|
||
* https://github.com/spring-projects/spring-boot/issues/8115[Expose the functional bean registration API via `SpringApplication`]
|
||
|
||
==== Kotlin
|
||
|
||
* https://youtrack.jetbrains.com/issue/KT-6380[Parent issue for Spring Framework support]
|
||
* https://youtrack.jetbrains.com/issue/KT-15667[Support "::foo" as a short-hand syntax for bound callable reference to "this::foo"]
|
||
* https://youtrack.jetbrains.com/issue/KT-11235[Allow specifying array annotation attribute single value without arrayOf()]
|
||
* https://youtrack.jetbrains.com/issue/KT-5464[Kotlin requires type inference where Java doesn't]
|
||
* 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]
|