1. Overview

A big announcement was made back in January in the Spring ecosystem:Kotlin support is coming to Spring Framework 5. This means that Spring Boot 2.x will have first-class support for Kotlin.

This is of course not unexpected, as the team at Pivotal is known for the acceptance of JVM languages such as Scala and Groovy.

Let’s build a Kotlin app using Spring Boot app 2.x!

2. Setup

2.1. Environment

Kotlin supports development inIntelliJ,Eclipse, and on thecommand line. Follow the directions to set up your environment, based on your preferences.

2.2. Setup

首先,让我们创建一个弹簧启动2项目和密苏里州dify the POM to contain entries specifying the versions of Java and Kotlin with the dependencies:

 org.jetbrains.kotlin kotlin-stdlib-jre8 1.2.71   org.jetbrains.kotlin kotlin-reflect 1.2.71   com.fasterxml.jackson.module jackson-module-kotlin 2.9.9 

Take note that we are specifying file locations for our Kotlin source and test files:

${project.basedir}/src/main/kotlin ${project.basedir}/src/test/kotlin

If our Kotlin files are in different locations, you will need to modify these entries in the POM.

To compile Kotlin modules and sources, we need to usekotlin-maven-plugin:

 kotlin-maven-plugin org.jetbrains.kotlin 1.1.2   spring  1.8    compile compile  compile    test-compile test-compile  test-compile      org.jetbrains.kotlin kotlin-maven-allopen 1.1.2   

Alright, now we have everything we need to build our Kotlin application. For reference: you can find the latest versions ofMaven Central(spring-boot-starter-web,kotlin-stdlib-jre8,kotlin-reflect,jackson-module-kotlin,test).

Next, let’s set up our application context.

3. Application Context

Let’s jump into some Kotlin code and write our familiar Spring Boot application context:

@SpringBootApplication class KotlinDemoApplication fun main(args: Array) { SpringApplication.run(KotlinDemoApplication::class.java, *args) }

We see our familiar@SpringBootApplicationannotation. This is the same annotation we would use in a Java class.

Below that we have a class definition for ourKotlinDemoApplicationclass. In Kotlin the default scope for classes is public so that we can omit that. Additionally, if a class has no variables and no functions, it can be declared without curly braces. So, in essence, we have just defined a class.

Moving on to the method. This is the standard Java entry point method, in Java:public static void main(String[] args).

Again, methods or functions are public by default, so we do not have to declare that here. Additionally,functions that do not return anything do not need to specify a void return type.

And finally,any function defined outside the body of a class is automatically static. This makes this function eligible for startup execution.

Now let’s run our application from the root directory usingmvn spring-boot: run. The application should start, and we should see our application running on port 8080.

Next, let’s build a controller.

4. Controller


@RestController class HelloController { @GetMapping("/hello") fun helloKotlin(): String { return "hello world" } }

Not too much different from a standard Spring controller but certainly less code. Let’s add a test class and case for this controller to validate our work:

@RunWith(SpringRunner::class) @SpringBootTest(classes = arrayOf(KotlinDemoApplication::class), webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) class KotlinDemoApplicationTests { @Autowired lateinit var testRestTemplate: TestRestTemplate @Test fun whenCalled_shouldReturnHello() { val result = testRestTemplate // ... .getForEntity("/hello", String::class.java) assertNotNull(result) assertEquals(result?.statusCode, HttpStatus.OK) assertEquals(result?.body, "hello world") } }

This test shows off one of Kotlin’s very powerful features – null safety! Kotlin variables that can be null must be declared using ‘?’. The compiler then knows that defensive coding is required before accessing that property.

In our test,TestRestTemplateis defined as a nullable type, and each time we access it, we do so using the null coalescing operator “?.” – which will return null if the called object is null.

This clarifies the use of nulls in the program and forces developers to write safe code when working with them.

Next, let’s add a service and integrate that into our controller.

5. Service

As you can probably guess now, our service is going to be pretty easy to add to our project. Let’s do that now:

@Service class HelloService { fun getHello(): String { return "hello service" } }

Pretty simple service here with a single function returning a String. Next, let’s wire our service into the controller and use it to return a value:

@RestController class HelloController(val helloService: HelloService) { // ... @GetMapping("/hello-service") fun helloKotlinService(): String { return helloService.getHello() } }

Ahh, that looks nice! In Kotlin the main constructor can be defined inline with the class declaration. We’ve omitted the@Autowiredannotation from our constructor because it’s not obligatory since some time.

Those parameters are automatically converted to fields in the class. Kotlin they are called properties. There are no getters or setters defined; they are created automatically. You can, of course, override these defaults if you want.

In Kotlin, properties in classes and variables in functions can be defined usingvarorval.Varindicates a mutable property, andvalindicates a final one.This allows the compiler to check for illegal access. Since ourHelloServiceis a singleton, we wire it up as avalto prevent mutation.

Next, let’s add a test for this controller method:

@Test fun whenCalled_shouldReturnHelloService() { var result = testRestTemplate // ... .getForEntity("/hello-service", String::class.java) assertNotNull(result) assertEquals(result?.statusCode, HttpStatus.OK) assertEquals(result?.body, "hello service") }

Lastly, let’s look at what a POJO looks like in Kotlin.

6. Kotlin Data Class

In Java, we represent data objects with plain old Java objects, the POJO. In Kotlin we have something that lets us express this type of object more concisely – a data class.

Let’s write a data object to return in our controller:

data class HelloDto(val greeting: String)

That was no trick. I’m not omitting anything from our class. With the data modifier, we get a lot of benefits. This keyword automatically creates anequals/hashcodepair, atoStringfunction, and a copy function. All that from a 53 character one-liner!

Now let’s add a method to return our new data class:

// ... @GetMapping("/hello-dto") fun helloDto(): HelloDto { return HelloDto("Hello from the dto") }

The data modifier does not add a default constructor, which is important for certain libraries like Jackson. To support this type of class we have added thejackson-module-kotlinto our POM file to support marshaling. This was done during section 2, and you can see the dependency there.

Finally, let’s add a test for this controller function:

@Test fun whenCalled_shoudlReturnJSON() { val result = testRestTemplate // ... .getForEntity("/hello-dto", HelloDto::class.java) assertNotNull(result) assertEquals(result?.statusCode, HttpStatus.OK) assertEquals(result?.body, HelloDto("Hello from the dto")) }

7. Conclusion

In this article, we looked at Kotlin support in Spring Boot 2.x. We saw from examples that Kotlin could simplify and enhance our applications by forcing us to write shorter, safer code.

Kotlin also supports some amazing features like the data class, class extensions, and is fully compatible with existing Java code. This means that you can write Kotlin code and call it from your Java classes and vice-versa. In addition, Kotlin was built from the ground up to have fantastic support in an IDE, and it does.

There are a lot of reasons to try Kotlin out, and with Google and Spring backing it, now is the time to check it out. Let us know what you decided to build using it!

Always, you can find the source codeover on GitHub.

Comments are closed on this article!