Kotlin also has a compiler for the JVM platform. All the codes we write in Kotlin can run on the JVM. Therefore, we can write Spring applications in Kotlin with all its features. However, Kotlin and Java are different languages. When combining them, some difficulties may arise. Since the Spring framework is written entirely in Java, all Kotlin Spring projects require a little extra setup. This topic shows them.
Additional dependencies
Suppose we have created a new Spring project in Kotlin, for example, using start.spring.io. This project needs to be configured further. Spring boot requires 2 Kotlin-specific libraries, which must be added for the app to work correctly:
kotlin-reflect: Spring often uses reflection, for example, to create proxy classes, but Java and Kotlin objects are different. Therefore, we have to work with them differently. This dependency adds the ability for Spring to work with Kotlin objects by reflection.jackson-module-kotlin: Java and Kotlin objects have different internal structures. The standard Jackson serializer for Java may not handle Kotlin objects. This dependency adds support for serialization/deserialization of Kotlin classes.
We can add them like this:
dependencies {
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
}As we can see, we only need to add these dependencies. We do not need to configure these libraries. Spring will do it for us.
All-open plugins
Spring works with most of our classes. When we start our project in Spring, it creates wrappers (proxy classes) over them (using inheritance) and can override any methods in our classes. In Kotlin, classes and their members are final by default. Therefore, if we do nothing, problems may arise in the functioning of the Spring. To avoid them, we need to mark everything with an open modifier. That is boilerplate work, and we can prevent it.
To do this, we will use the compiler plugin named "all-open" plugin. It adapts Kotlin to the requirements of Spring and makes classes annotated with a specific annotation and their members open without the explicit open keyword. We can add this plugin like this:
apply plugin: "kotlin-allopen"
buildscript {
dependencies {
classpath "org.jetbrains.kotlin:kotlin-allopen:$kotlin_version"
}
}As an alternative, we can enable it using the plugins block:
plugins {
id "org.jetbrains.kotlin.plugin.allopen" version "$kotlin_version"
}So, we can say that these plugin annotations will make classes public. We need to specify these annotations in a particular allOpen block.
allOpen {
annotation("org.hyperskill.demo.MakeThisClassAsOpen")
annotations("org.hyperskill.demo.FirstAnnotation", "org.hyperskill.demo.SecondAnnotation")
}Note that the allOpen block must contain the full name of the annotation class. For example, if the annotation is named MyAnnotation and is in the com.yourproject.app package, we would specify com.yourproject.app.MyAnnotation .
If the class (or any of its superclasses) is annotated with indicated annotations, the class, and its members will become open.
@FirstAnnotation
class MyKotlinClass { // will be open
var key: String = "" // will be open
fun someMethod() { // will be open
// . . .
}
}
class ExtendedClass : MyKotlinClass // will be openIf we create our annotation and annotate it with the specified annotation in the all-open block, then it will work in the same way as if we specified it in the allOpen block.
@FirstAnnotation
annotation class MySpringAnnotation
@MySpringAnnotation
class SomeClass // will be openThis plugin is quite popular in Spring and has an extension for Spring. We can enable the Kotlin-Spring compiler plugin. Kotlin-Spring is a wrapper on top of the all-open, and it behaves the same way:
apply plugin: "kotlin-spring" // instead of "kotlin-allopen"
buildscript {
dependencies {
classpath "org.jetbrains.kotlin:kotlin-allopen:$kotlin_version"
}
}
Or this way:
plugins {
id "org.jetbrains.kotlin.plugin.spring" version "$kotlin_version"
}This advanced plugin includes the following annotations by default:
@Component@Transactional@SpringBootTest@Async@Cacheable
These annotations already annotate some Spring annotations, therefore @Configuration, @Controller, @RestController, @Service, @Repository, and some others will be as all-open annotations.
Note that if you use the project template generated by start.spring.io, the Kotlin-Spring plugin will be enabled by default.
We can read more about this plugin in the official all-open compiler plugin documentation.
Running an application
Let's run our application. Kotlin allows us to run Spring applications using a more concise and pleasant syntax:
@SpringBootApplication
class Application
fun main(args: Array<String>) {
runApplication<Application>(*args)
}As you can see, runApplication is one of the many functions supplied by Spring for Kotlin.
Adding Kotlin to an existing project
Suppose you want to add Kotlin to an existing project. Luckily, Kotlin is Java compatible, so it's easy to do this. For it, you need to add only one dependency:
plugins {
//...
id 'org.jetbrains.kotlin.jvm' version '1.8.22'
}As you can see, it's simple! After this, you can use any Kotlin code in the application. Note that this is the minimum required setting for the project. The Spring application will run with this dependency, and some pieces of Kotlin code can be used in the project. We are still not immune from errors, so we will need to do our additional configuration, as described above.
If you are creating a new project, instead of configuring it manually, you can use some service that can generate a basic Spring Boot project with Kotlin support, such as start.spring.io
Gradle migration
Gradle allows you to write your scripts in 2 languages: Groovy and Kotlin. The most commonly used one is Groovy. However, in Gradle, Kotlin is used more often. And we can use it too! To do this, we need to transfer our scripts from Groovy to Kotlin. First, we need to rename the Gradle file from build.gradle to build.gradle.kts. Then you need to adapt your Groovy scripts for Kotlin.
For example, adding the previous dependency would look like this:
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
//...
kotlin ("jvm") version "1.8.22"
}You can read more about the Gradle migration on the official Gradle site.
It doesn't matter if you use Groovy DSL or Kotlin DSL, the application will run anyway. Migration is also optional.
Conclusion
In this topic, you learned about the additional configuration of Spring applications in Kotlin. As you have seen, it is not difficult. All you need to do is:
Add three other dependencies to the project.
Use an all-open plugin.
Do these two small steps to eliminate strange errors while working with Spring projects.