Introducing Kotlin support in Spring Framework 5.0
Following the Kotlin support on start.spring.io we introduced a few months ago, we have continued to work to ensure that Spring and Kotlin play well together. One of the key strengths of Kotlin is that it provides a very good interoperability with libraries written in Java. But there are ways to go even further and allow writing fully idiomatic Kotlin code when developing your next Spring application. In addition to Spring Framework support for Java 8 that Kotlin applications can leverage like functional web or bean registration APIs, there are additional Kotlin dedicated features that should allow you to reach a new level of productivity.
That’s why we are introducing a dedicated Kotlin support in Spring Framework 5.0 M4 , and I would like to summarize in this blog post the features that are designed to make your developer experience seamless when using these technologies together. You can use this link to find Kotlin related issues in Spring Framework bug tracker.
Leveraging Kotlin nullable information
Originally based on a community contribution from
Raman Gupta
, Spring now takes advantage of
Kotlin null-safety support
to determine if an HTTP parameter is required without having to define explicitly the
required
attribute. That means
@RequestParam name: String?
with be treated as not required and
@RequestParam name: String
as required. This is also supported on Spring Messaging
@Header
annotation.
In a similar fashion, Spring bean injection with
@Autowired
or
@Inject
uses this information to know 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.
Extensions for RestTemplate and Functional Web API
Kotlin extensions allow to extend existing APIs in a non-intrusive way, providing a better alternative to utility classes or Kotlin specific class hierarchies to add Kotlin dedicated features to Spring. Some libraries like KotlinPrimavera from Mario Arias have already showed various kind of Kotlin helpers we can bring to Spring API in order to allow writing more idiomatic code. With Spring Framework 5, we integrate the most useful and popular extensions in Spring Framework dependencies, and we are adding new ones!
For example, Kotlin reified type parameters provide a workaround for JVM generics type erasure , so we have introduced some extensions to take advantage of this feature to provide a better API when possible.
That allows to provide convenient API for
RestTemplate
(thanks to
Jon Schneider
from Netflix for contributing this). For example, to retrieve a list of
Foo
objects in Java you have to write:
List<Foo> result = restTemplate.exchange(url, HttpMethod.GET, null, new ParameterizedTypeReference<List<Foo>>() { }).getBody();
Or that way if you use an intermediate array:
List<Foo> result = Arrays.asList(restTemplate.getForObject(url, Foo[].class));
While in Kotlin with Spring Framework 5 extensions you will be able to write:
val result : List<Foo> = restTemplate.getForObject(url)
Be aware that Kotlin extensions are statically resolved, you have to import them. In the example above, you need to add
import org.springframework.web.client.RestOperationsExtension.getForObject
to be able to use it. Kotlin extensions are generally automatically suggested by IDEs like IntelliJ IDEA (like static imports), but it does not work yet for extensions nested in a container
object
(you can vote for
KT-15440
) so until it is fixed you will have to add Spring Kotlin extensions imports manually.
Available
RestTemplate
or functional web API extensions currently available in Spring Framework 5.0 M4 are:
- RestOperationsExtension
- ServerRequestExtension
- BodyInsertersExtension
- BodyExtractorsExtension
- ClientResponseExtension
These extensions also provide member functions supporting natively Kotlin
KClass
, allowing you to specify
Foo::class
parameter instead of
Foo::class.java
.
Reactor Kotlin extensions
Reactor is the reactive foundation Spring Framework 5.0 is built upon, and there is good chances your are going to use its Mono , Flux and StepVerifier APIs when developing a reactive web application.
So today we are also introducing Kotlin support in Reactor, via the new
reactor-kotlin
project! It provides extensions to be able to create
Mono
instances from any class instance by writing
foo.toMono()
which many will prefer to
Mono.just(foo)
. It also supports for example creating a
Flux
from a Java 8
Stream
instance with
stream.toFlux()
.
Iterable
,
CompletableFuture
and
Throwable
extensions as well as
KClass
based variants of Reactor API are also provided.
This is still the early days of this project, so feel free to contribute your own extensions if you find missing bits.
Functional bean registration with Kotlin
Spring Framework 5.0 introduces a new way to register beans using lambda as an alternative to XML or JavaConfig with
@Configuration
and
@Bean
. In a nutshell, it makes it possible to register beans with a
Supplier
lambda that acts as a
FactoryBean
.
In Java you will for example write:
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.registerBean(Foo.class);
context.registerBean(Bar.class, () -> new
Bar(context.getBean(Foo.class))
);
While in Kotlin, reified type parameters allow us to simply write:
val context = AnnotationConfigApplicationContext()
context.registerBean(Foo::class)
context.registerBean(Supplier {
Bar(context.getBean(Foo::class))
})
You can see a concrete example of Spring application using both functional web and bean registration API at https://github.com/mix-it/mixit/ .
ApplicationContext
related Kotlin extensions available are:
No need to declare your bean class as open anymore
Until now, one of the few pain points you faced when building a Spring Boot application with Kotlin was the need to add an
open
keyword on each class and their member functions of Spring beans proxified with CGLIB like
@Configuration
classes. The root cause of this requirement comes from the fact that in Kotlin,
classes are final by default
.
Fortunately, Kotlin 1.0.6 now provides a
kotlin-spring
plugin that open classes and their member functions by default for classes annotated or meta-annotated with one of the following annotation:
-
@Component
-
@Async
-
@Transactional
-
@Cacheable
Meta-annotations support means that classes annotated with
@Configuration
,
@Controller
,
@RestController
,
@Service
or
@Repository
are automatically opened since these annotations are meta-annotated with
@Component
.
We have updated
start.spring.io
to enabled it by default. You can have a look to
this Kotlin 1.0.6 blog post
for more details, including the new
kotlin-jpa
and
kotlin-noarg
plugins really useful with Spring Data entities.
Kotlin based Gradle build configuration
Back in May, Gradle announced they are going to support writing build and config files in Kotlin in addition to Groovy. This make it possible to have full auto-completion and validation in your IDE, because such files are regular statically-typed Kotlin Script files. This is likely to become the natural choice for Kotlin based project, but this is also valuable for Java projects too.
Since May, the gradle-script-kotlin project has continued to evolve, and is now usable with 2 warnings to keep in mind:
-
You need Kotlin 1.1-EAP IDEA plugin to get autocompletion (but wait Kotlin
1.1-M05
if you are usingkotlin-spring
plugin since1.1-M04
does not work with this plugin yet) - The documentation is not complete, but the Gradle team is really helpful on the #gradle channel of the Kotlin Slack.
Both spring-boot-kotlin-demo and mixit projects use such Kotlin based Gradle builds, so feel free to have a look. We are discussing adding such support on start.spring.io.
Kotlin Script based templates
As of version 4.3, Spring Framework provides a ScriptTemplateView to render templates using script engines that supports JSR-223 . Kotlin 1.1-M04 provides such support and allows to render Kotlin based templates, see this commit for details.
This enables some interesting use cases like writing type-safe templates using
kotlinx.html
DSL or simply Kotlin multiline
String
with interpolation, as demonstrated in this
kotlin-script-templating
project. This could allow you to write this kind of templates with full autocompletion and refactoring support in your IDE:
import io.spring.demo.User
import io.spring.demo.joinToLine
"""
${include("header", bindings)}
<h1>Title : $title</h1>
<ul>
${(users as List<User>).joinToLine{ "<li>User ${it.firstname} ${it.lastname}</li>" }}
</ul>
${include("footer")}
"""
This feature is still work in progress, but I am collaborating with the Kotlin team to tentatively make it production ready with nested template and i18n support on both MVC and Reactive sides for Spring Framework 5.0 GA.
Conclusion
The more I write Spring Boot applications with Kotlin, the more I feel these 2 technologies share the same mindset and allow you to write applications more efficiently with expressive, short and readable code, and Spring Framework 5 Kotlin support is a significant step towards combining these technologies in a natural, simple and powerful way.
Kotlin can be used to write annotation-based Spring Boot applications , but will also be a good fit with the new kind of functional and reactive applications that Spring Framework 5.0 will enable.
Kotlin team did a great job to fix most almost all the pain points we reported, so big thanks to them. The upcoming Kotlin 1.1 release is expected to also fix
KT-11235
in order to allow specifying array annotation attribute single value without
arrayOf()
. The main remaining issue you will face is probably
KT-14984
hat will require specifying explicitly lambda type (like
Supplier { }
or
HandlerFunction { }
where just specifying
{ }
should be enough.
Feel free to test Spring Framework 5.0 Kotlin support by going to
start.spring.io
and generating a Spring Boot
2.0.0 (SNAPSHOT)
project and send us your feedback here or in the
#spring
channel of
Kotlin Slack
. You can also
contribute
the Kotlin extensions you need ;-)