Building a Spring Boot Web Application with Kotlin
Overview
This article creates a simple Spring Boot web application with Kotlin.
Create the Project
Use the curl command below to create an initial Spring Boot project.
curl https://start.spring.io/starter.tgz \
-d bootVersion=2.4.11 \
-d dependencies=web \
-d baseDir=spring-boot-hello-world \
-d groupId=com.devkuma \
-d artifactId=spring-boot-hello-world \
-d packageName=com.devkuma.hello \
-d applicationName=HelloApplication \
-d packaging=jar \
-d language=kotlin \
-d javaVersion=11 \
-d type=gradle-project | tar -xzvf -
Running this command creates a web project with Java 11 and Spring Boot 2.4.11.
The generated project has the following file structure.
.
├── HELP.md
├── build.gradle.kts
├── gradle
│ └── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle.kts
└── src
├── main
│ ├── kotlin
│ │ └── com
│ │ └── devkuma
│ │ └── hello
│ │ └── HelloApplication.kt
│ └── resources
│ ├── application.properties
│ ├── static
│ └── templates
└── test
└── kotlin
└── com
└── devkuma
└── hello
└── HelloApplicationTests.kt
Check the Generated Project
build.gradle.kts
The generated build file uses the .kts extension, which means it is a Kotlin script file.
(For reference, Java Gradle build files use the .gradle extension and are written in Groovy.)
/build.gradle.kts
// ... omitted ...
dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
testImplementation("org.springframework.boot:spring-boot-starter-test")
}
// ... omitted ...
Looking at the dependency libraries in the file, you can see that the Spring Framework (org.springframework.boot), Jackson for Kotlin (com.fasterxml.jackson.module), and Kotlin libraries (org.jetbrains.kotlin) have been added.
Application Context
/src/main/kotlin/com/devkuma/hello/controller/HelloController.kt
package com.devkuma.hello
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
@SpringBootApplication
class HelloApplication
fun main(args: Array<String>) {
runApplication<HelloApplication>(*args)
}
You can confirm that the @SpringBootApplication annotation and the main function for running Spring Boot are present.
(Overall, it looks similar to Java code.)
Create a Controller
Create a Controller file and write it as follows.
/src/main/kotlin/com/devkuma/hello/controller/HelloController.kt
package com.devkuma.hello.controller
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RestController
@RestController
class HelloController {
@GetMapping("/hello")
fun hello(): String {
return "hello world"
}
}
Create and run the test code.
/src/test/kotlin/com/devkuma/hello/controller/HelloControllerTests.kt
package com.devkuma.hello.controller
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.ResultActions
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
import org.springframework.test.web.servlet.result.MockMvcResultHandlers.print
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.content
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
@WebMvcTest(HelloController::class)
class HelloControllerTests {
@Autowired
lateinit var mockMvc: MockMvc
@Test
@DisplayName("Hello")
fun hello() {
// when
val resultActions: ResultActions = mockMvc.perform(get("/hello"))
.andDo(print())
// then
resultActions
.andExpect(status().is2xxSuccessful)
.andExpect(content().string("hello world"))
.andDo(print())
}
}
Check that the test runs without errors.
To verify the application behavior directly, run the project and check it with the following curl command.
% curl localhost:8080/hello
hello world%
Create a Service
Create a Service file and write it as follows.
/src/main/kotlin/com/devkuma/hello/service/HelloService.kt
package com.devkuma.hello.service
import org.springframework.stereotype.Service
@Service
class HelloService {
fun getHello(): String {
return "hello service"
}
}
Add helloService() to the Controller file as follows.
/src/main/kotlin/com/devkuma/hello/controller/HelloController.kt
package com.devkuma.hello.controller
import com.devkuma.hello.service.HelloService
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RestController
@RestController
class HelloController(val helloService: HelloService) {
// ... omitted ...
@GetMapping("/hello-service")
fun helloService(): String {
return helloService.getHello()
}
}
Add and run the helloService() test code as follows.
/src/test/kotlin/com/devkuma/hello/controller/HelloControllerTests.kt
package com.devkuma.hello.controller
import com.devkuma.hello.service.HelloService
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import org.mockito.BDDMockito.given
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.boot.test.mock.mockito.MockBean
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.ResultActions
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
import org.springframework.test.web.servlet.result.MockMvcResultHandlers.print
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.content
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
@WebMvcTest(HelloController::class)
class HelloControllerTests {
@Autowired
lateinit var mockMvc: MockMvc
@MockBean
lateinit var helloService: HelloService
// ... omitted ...
@Test
@DisplayName("Hello Service")
fun helloService() {
// given
given(helloService.getHello()).willReturn("hello service");
// when
val resultActions: ResultActions = mockMvc.perform(get("/hello-service"))
.andDo(print())
// then
resultActions
.andExpect(status().is2xxSuccessful)
.andExpect(content().string("hello service"))
.andDo(print())
}
}
To verify the application behavior directly, run the project and check it with the following curl command.
% curl localhost:8080/hello-service
hello service%
Create a DTO
Create a DTO file and write it as follows.
/src/main/kotlin/com/devkuma/hello/dto/HelloDto.kt
package com.devkuma.hello.dto
class HelloDto(val greeting: String)
Add helloDto() to the Controller file as follows.
/src/main/kotlin/com/devkuma/hello/controller/HelloController.kt
@RestController
class HelloController(val helloService: HelloService) {
// ... omitted ...
@GetMapping("/hello-dto")
fun helloDto(): HelloDto {
return HelloDto("hello dto")
}
}
Add and run the helloDto() test code as follows.
/src/test/kotlin/com/devkuma/hello/controller/HelloControllerTests.kt
package com.devkuma.hello.controller
import com.devkuma.hello.service.HelloService
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import org.mockito.BDDMockito.given
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.boot.test.mock.mockito.MockBean
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.ResultActions
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
import org.springframework.test.web.servlet.result.MockMvcResultHandlers.print
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.*
@WebMvcTest(HelloController::class)
class HelloControllerTests {
// ... omitted ...
@Test
@DisplayName("Hello DTO")
fun helloDto() {
// when
val resultActions: ResultActions = mockMvc.perform(get("/hello-dto"))
.andDo(print())
// then
resultActions
.andExpect(status().is2xxSuccessful)
.andExpect(jsonPath("greeting").value("hello dto"))
.andDo(print())
}
}
To verify the application behavior directly, run the project and check it with the following curl command.
% curl localhost:8080/hello-dto
{"greeting":"hello dto"}%
More
- This post was rewritten based on Baeldung: Spring Boot and Kotlin.
- All source code above is available on GitHub.