Skip to content

Commit a8693bf

Browse files
committed
Improve Kotlin ref doc
This commit add a section about annotations and provides various update and enhancements to the Kotlin reference documentation. Issue: SPR-15659
1 parent ec6475a commit a8693bf

File tree

1 file changed

+91
-37
lines changed

1 file changed

+91
-37
lines changed

src/docs/asciidoc/kotlin.adoc

Lines changed: 91 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,19 @@
77

88
== Introduction
99

10-
https://kotlinlang.org[Kotlin] is a statically-typed language targeting the JVM which allows to write concise and elegant
11-
code while providing a very good https://kotlinlang.org/docs/reference/java-interop.html[interoperability] with libraries
10+
https://kotlinlang.org[Kotlin] is a statically-typed language targeting the JVM (and other platforms)
11+
which allows to write concise and elegant code while providing a very good
12+
https://kotlinlang.org/docs/reference/java-interop.html[interoperability] with libraries
1213
written in Java.
1314

14-
Spring Framework 5 introduces first-class support for Kotlin and allows developers to write Spring + Kotlin
15-
applications almost like if Spring Framework was a native Kotlin framework.
15+
Spring Framework 5 introduces first-class support for Kotlin and allows developers to write
16+
Spring + Kotlin applications almost like if Spring Framework was a native Kotlin framework.
1617

1718
== Requirements ==
1819

19-
Spring Framework 5 supports Kotlin 1.1+ and requires https://bintray.com/bintray/jcenter/org.jetbrains.kotlin%3Akotlin-stdlib[`kotlin-stdlib`] (or one of its
20-
https://bintray.com/bintray/jcenter/org.jetbrains.kotlin%3Akotlin-stdlib-jre7[`kotlin-stdlib-jre7`]
20+
Spring Framework supports Kotlin 1.1+ and requires
21+
https://bintray.com/bintray/jcenter/org.jetbrains.kotlin%3Akotlin-stdlib[`kotlin-stdlib`]
22+
(or one of its https://bintray.com/bintray/jcenter/org.jetbrains.kotlin%3Akotlin-stdlib-jre7[`kotlin-stdlib-jre7`]
2123
/ https://bintray.com/bintray/jcenter/org.jetbrains.kotlin%3Akotlin-stdlib-jre8[`kotlin-stdlib-jre8`] variants)
2224
and https://bintray.com/bintray/jcenter/org.jetbrains.kotlin%3Akotlin-reflect[`kotlin-reflect`]
2325
to be present on the classpath. They are provided by default if you bootstrap a Kotlin project on
@@ -47,6 +49,12 @@ and Spring Framework provides some extensions to take advantage of this feature.
4749
That allows to provide a better Kotlin API `RestTemplate`, the new `WebClient` from Spring
4850
WebFlux and for various other API.
4951

52+
[NOTE]
53+
====
54+
Other libraries like Reactor or Spring Data also provide Kotlin extensions for their API
55+
in order to allow a better Kotlin development experience.
56+
====
57+
5058
To retrieve a list of `Foo` objects in Java you have to write:
5159

5260
[source,java]
@@ -66,13 +74,6 @@ val users : Flux<User> = client.get().retrieve().bodyToFlux()
6674
Like in Java, `users` in Kotlin is strongly typed, but Kotlin clever type inference allows
6775
shorter syntax.
6876

69-
70-
[NOTE]
71-
====
72-
Other libraries like Reactor or Spring Data also provide Kotlin extensions for their API
73-
in order to allow a better Kotlin development experience.
74-
====
75-
7677
== Null-safety
7778

7879
One of Kotlin's key features is https://kotlinlang.org/docs/reference/null-safety.html[null-safety]
@@ -82,15 +83,15 @@ declarations, expressing "value or no value" semantics without paying the cost o
8283
(Kotlin allows using functional constructs with nullable values; check out this
8384
http://www.baeldung.com/kotlin-null-safety[comprehensive guide to Kotlin null-safety].)
8485

85-
Although Java does not allow to express null-safety in its type-system, Spring Framework 5 introduces
86-
https://jira.spring.io/browse/SPR-15540[null-safety of the whole Spring Framework APIs]
86+
Although Java does not allow to express null-safety in its type-system, Spring Framework now
87+
provides https://jira.spring.io/browse/SPR-15540[null-safety of the whole Spring Framework API]
8788
via tooling-friendly annotations:
8889

89-
* `@NonNullApi` annotations at package level declare that non-null is the default behavior
90+
* `@NonNullApi` annotations at package level declare non-null as the default behavior
9091
* `@Nullable` annotations where specific parameters or return values can be `null`.
9192

9293
Both annotations are meta-annotated with https://jcp.org/en/jsr/detail?id=305[JSR 305]
93-
meta-annotations (a dormant JSR but supported by tools like IDEA, Eclipse, Findbugs, etc.)
94+
meta-annotations (a dormant JSR but supported by tools like IDEA, Findbugs, etc.)
9495
to provide useful warnings to Java developers.
9596

9697
On the Kotlin side - as of the https://blog.jetbrains.com/kotlin/2017/08/kotlin-1-1-4-is-out/[Kotlin 1.1.4 release] -
@@ -106,9 +107,6 @@ the default behavior in an upcoming release of Kotlin.
106107
Make sure to https://github.com/sdeleuze/spring-kotlin-functional/blob/2d6ac07adfc2b8f25e91681dbb2b58a1c6cdf9a7/build.gradle.kts#L57[include JSR-305 JAR]
107108
until Kotlin 1.1.5 is released (it will fix https://youtrack.jetbrains.com/issue/KT-19419[KT-19419]).
108109

109-
Currently null-safety does not apply to generic type parameters, but that could change in
110-
the future, the related issue is https://youtrack.jetbrains.com/issue/KT-19592[KT-19592].
111-
112110
[NOTE]
113111
====
114112
Other libraries like Reactor or Spring Data leverage these annotations to provide
@@ -117,7 +115,7 @@ null-safe APIs for Kotlin developers.
117115

118116
== Classes & Interfaces
119117

120-
Spring Framework 5 now supports various Kotlin constructs like instantiating Kotlin classes
118+
Spring Framework supports various Kotlin constructs like instantiating Kotlin classes
121119
via primary constructors, immutable classes data binding and function optional parameters
122120
with default values.
123121

@@ -127,8 +125,8 @@ compiler flag.
127125

128126
https://github.com/FasterXML/jackson-module-kotlin[Jackson Kotlin module] which is required
129127
for serializing / deserializing JSON data is automatically registered when present in the
130-
classpath, and will log a warning message if Jackson + Kotlin are detected without Jackson
131-
Kotlin module.
128+
classpath, and a warning message will be logged if Jackson + Kotlin are detected without
129+
Jackson Kotlin module.
132130

133131
[NOTE]
134132
====
@@ -140,7 +138,6 @@ As of Spring Boot 2.0, Jackson Kotlin module is automatically provided via the J
140138
Spring Framework also takes advantage of https://kotlinlang.org/docs/reference/null-safety.html[Kotlin null-safety]
141139
to determine if an HTTP parameter is required without having to define explicitly the `required` attribute.
142140
That means `@RequestParam name: String?` with be treated as not required and `@RequestParam name: String` as required.
143-
144141
This is also supported on Spring Messaging `@Header` annotation.
145142

146143
In a similar fashion, Spring bean injection with `@Autowired` or `@Inject` uses this information
@@ -150,8 +147,8 @@ won’t raise an error if such bean does not exist.
150147

151148
== Bean definition DSL
152149

153-
Spring Framework 5 introduces a new way to register beans in a functional way using lambda
154-
as an alternative to XML or JavaConfig with `@Configuration` and `@Bean`. In a nutshell,
150+
Spring Framework 5 introduces a new way to register beans in a functional way using lambdas
151+
as an alternative to XML or JavaConfig (`@Configuration` and `@Bean`). In a nutshell,
155152
it makes it possible to register beans with a lambda that acts as a `FactoryBean`.
156153
It is very efficient and does not require any reflection or CGLIB proxies.
157154

@@ -176,8 +173,8 @@ val context = GenericApplicationContext().apply {
176173
}
177174
----
178175

179-
In order to allow a more declarative approach and cleaner syntax, Spring Framework 5 introduces
180-
a new {doc-root}/spring-framework/docs/{spring-version}/kdoc-api/spring-framework/org.springframework.context.support/-bean-definition-dsl/[Kotlin bean definition DSL]
176+
In order to allow a more declarative approach and cleaner syntax, Spring Framework provides
177+
a {doc-root}/spring-framework/docs/{spring-version}/kdoc-api/spring-framework/org.springframework.context.support/-bean-definition-dsl/[Kotlin bean definition DSL]
181178
It declares an `ApplicationContextInitializer` via a clean declarative API which allows
182179
you to deal with profiles and `Environment` for customizing how your beans are registered.
183180

@@ -240,16 +237,18 @@ for a concrete example.
240237

241238
[NOTE]
242239
====
243-
Spring Boot is based on Java Config, but should allow using user-defined functional bean definitions,
244-
see https://jira.spring.io/browse/SPR-13779[SPR-13779] and https://github.com/spring-projects/spring-boot/issues/8115[spring-boot/#8115]
240+
Spring Boot is based on Java Config and
241+
https://github.com/spring-projects/spring-boot/issues/8115[does not provide specific support for functional bean definition yet],
242+
but you can experimentally use functional bean definitions via its `ApplicationContextInitializer` support,
243+
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]
245244
for more details and up to date informations.
246245
====
247246

248247
== Web
249248

250249
=== WebFlux Functional DSL
251250

252-
Spring Framework 5 comes with a
251+
Spring Framework now comes with a
253252
{doc-root}/spring-framework/docs/{spring-version}/kdoc-api/spring-framework/org.springframework.web.reactive.function.server/-router-function-dsl/[Kotlin routing DSL]
254253
that allows you to leverage the <<reactive-web#webflux-fn,WebFlux functional API>> with clean and idiomatic Kotlin code:
255254

@@ -277,7 +276,7 @@ router {
277276
====
278277
This DSL is programmatic, thus also allows custom registration logic of beans via `if` expression,
279278
`for` loop or any other Kotlin constructs. That can be useful when routes need to be registered
280-
depending on dynamic data, for example created via the backoffice.
279+
depending on dynamic data (from a database for example).
281280
====
282281

283282
See https://github.com/mixitconf/mixit/tree/bad6b92bce6193f9b3f696af9d416c276501dbf1/src/main/kotlin/mixit/web/routes[MiXiT project routes]
@@ -291,7 +290,7 @@ to render templates using script engines that supports https://www.jcp.org/en/js
291290
and Spring Framework 5 go even further by extending this feature to WebFlux and supporting
292291
https://jira.spring.io/browse/SPR-15064[i18n and nested templates].
293292

294-
Kotlin 1.1 provides such support and allows to render Kotlin based templates, see
293+
Kotlin provides such support and allows to render Kotlin based templates, see
295294
https://github.com/spring-projects/spring-framework/commit/badde3a479a53e1dd0777dd1bd5b55cb1021cf9e[this commit] for details.
296295

297296
This enables some interesting use cases like writing type-safe templates using
@@ -318,6 +317,9 @@ project for more details.
318317

319318
== Spring projects in Kotlin
320319

320+
This section provides a focus on some specific hints and recommendations worth to know when
321+
developing Spring projects in Kotlin.
322+
321323
=== Final by default
322324

323325
By default, https://discuss.kotlinlang.org/t/classes-final-by-default/166[all classes in Kotlin are `final`].
@@ -349,7 +351,8 @@ http://start.spring.io/#!language=kotlin[start.spring.io] enables it by default.
349351

350352
=== Injecting dependencies
351353

352-
Try to favor constructor injection with `val` read-only https://kotlinlang.org/docs/reference/properties.html[properties].
354+
Try to favor constructor injection with `val` read-only (and non-nullable when possible)
355+
https://kotlinlang.org/docs/reference/properties.html[properties].
353356

354357
[source,kotlin]
355358
----
@@ -420,9 +423,59 @@ fun defaultPropertyConfigurer() = PropertySourcesPlaceholderConfigurer()
420423
If you are using Spring Boot, you would probably be interested in using
421424
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-typesafe-configuration-properties[`@ConfigurationProperties`]
422425
instead of `@Value`, but currently you have to use it with nullable `var` (which is far from ideal)
423-
properties since immutable classes initialized by constructor are not support yet.
424-
See https://github.com/spring-projects/spring-boot/issues/8762[this issue] for more details.
426+
properties since immutable classes initialized by constructor are not supported yet.
427+
See these issues about https://github.com/spring-projects/spring-boot/issues/8762[`@ConfigurationProperties` binding for immutable POJOs]
428+
and https://github.com/spring-projects/spring-boot/issues/1254[`@ConfigurationProperties` binding on interfaces]
429+
for more details.
430+
====
431+
432+
=== Annotation array attributes
433+
434+
Kotlin annotations are mostly similar to Java ones, but array attributes - which are
435+
extensively used in Spring - behaves differently. As explained in https://kotlinlang.org/docs/reference/annotations.html[Kotlin documentation]
436+
unlike other attributes, `value` attribute name can be omitted and when it is an array
437+
attribute it is specified as a `vararg` parameter.
438+
439+
To understand what that means more concretely, let's take `@RequestMapping`, which is one
440+
of the most used Spring annotation as an example. This Java annotation is declared as following:
441+
442+
[source,java]
443+
----
444+
public @interface RequestMapping {
445+
446+
@AliasFor("path")
447+
String[] value() default {};
448+
449+
@AliasFor("value")
450+
String[] path() default {};
451+
452+
RequestMethod[] method() default {};
453+
454+
// ...
455+
}
456+
----
457+
458+
The typical use case for `@RequestMapping` is to map an handler method to a specific path
459+
+ method. In Java, it is possible to specify single value for annotation array attribute,
460+
they will be automatically converted to arrays. That's why you can write
461+
`@RequestMapping(value = "/foo", method = RequestMethod.GET)` or
462+
`@RequestMapping(path = "/foo", method = RequestMethod.GET)`.
463+
464+
In Kotlin, you will have to write `@RequestMapping("/foo", method = arrayOf(RequestMethod.GET))`.
465+
The variant using `path` is not recommended as it need to be written
466+
`@RequestMapping(path = arrayOf("/foo"), method = arrayOf(RequestMethod.GET))`.
467+
468+
A workaround for this specific `method` attribute (the most common one) is to use shortcut
469+
annotation like `@GetMapping`, `@PostMapping`, etc.
470+
471+
[NOTE]
425472
====
473+
Remininder: if you don't specify `@RequestMapping` `method` attribute, all HTTP methods will be matched,
474+
not just `GET` ones.
475+
====
476+
477+
Improving syntax and consistency of Kotlin annotation array attributes is discussed in
478+
https://youtrack.jetbrains.com/issue/KT-11235[this Kotlin language design issue].
426479

427480
=== Testing
428481

@@ -523,7 +576,8 @@ Here is a list of pending issues related to Spring + Kotlin support.
523576
==== Spring Boot
524577

525578
* https://github.com/spring-projects/spring-boot/issues/5537[Improve Kotlin support]
526-
* https://github.com/spring-projects/spring-boot/issues/8762[Allow @ConfigurationProperties binding for immutable POJOs]
579+
* https://github.com/spring-projects/spring-boot/issues/8762[Allow `@ConfigurationProperties` binding for immutable POJOs]
580+
* https://github.com/spring-projects/spring-boot/issues/1254[Allow `@ConfigurationProperties` binding on interfaces]
527581
* https://github.com/spring-projects/spring-boot/issues/8511[Provide support for Kotlin KClass parameter in `SpringApplication.run()`]
528582
* https://github.com/spring-projects/spring-boot/issues/8115[Expose the functional bean registration API via `SpringApplication`]
529583

0 commit comments

Comments
 (0)