Hello Guys.
Today we will talk about the new feature added in Spring 5.0 and Spring Boot 2. We will understand the Kotlin support for Spring Boot Applications.
Kotlin is a new language created by JetBrains Team. The language is JVM language. It means the language creates a bytecode to run on Java Virtual Machine.
As we can see, the primary inspiration is Scala language. There are many constructions similar in both languages, the data classes concepts for instance.
There are some interesting advantages when we adopt Kotlin to code. The most exciting is reduce boilerplate code and make our code more concise, it brings more maintainability and transforms our code more readable.
We will understand these topics on next examples, then is Time to Code!!!
Create the Tasks Project with Spring Initializr
We will create a simple project to manage Tasks. The main idea here is to explain some Kotlin interesting points on this project.
Let’s go to Spring Initiliazr page. The project should be created with these configurations below:
The interesting points here are:
- Maven Project
- Kotlin Language
- Spring Boot 2
- Dependencies: Reactive Web, Actuator and Reactive MongoDB
Creating our TaskEntity
We need to create our main entity. The entity is pretty simple and so easy to implement the Task class should be like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
package tech.claudioed.tasks.domain import org.springframework.data.annotation.Id import org.springframework.data.mongodb.core.mapping.Document /** * @author claudioed on 25/03/18. * Project tasks */ @Document(collection = "tasks") data class Task(@Id val id:String,val owner:String, val description:String, val projectId:String) data class CreateTaskRequest(val owner:String, val description:String, val projectId:String) |
As we can see, there are some Kotlin interesting points here. There is a data class keyword it means the class has the purpose of holding data. Kotlin will add some important behaviors automatically like:
- equals() and hashCode()
- toString()
- copy()
there are some restrictions the data classes cannot be abstract, open, sealed or inner.
The full data classes documentation can be found here.
Creating the Reactive Task Repository
Now, we will use the Spring Data MongoDB Reactive implementation. The behaviors are similar in the blocking version, but it will not blocking because is the reactive version. The way to thinking is similar there is DSL to use objects properties to create queries automatically.
The TaskRepository should be like this:
1 2 3 4 5 6 7 8 9 10 |
package tech.claudioed.tasks.domain.repository import org.springframework.data.repository.reactive.ReactiveCrudRepository import tech.claudioed.tasks.domain.Task /** * @author claudioed on 25/03/18. * Project tasks */ interface TaskRepository:ReactiveCrudRepository<Task,String> |
The keyword interface is the same in Java language, but the way to extends is Kotlin is slightly different, in Kotlin we will use “:” instead of extends.
Creating the TaskService
Let’s create our TaskService class it will invoke the repository implementations. The code of TaskService should be like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
package tech.claudioed.tasks.domain.service import org.springframework.stereotype.Service import tech.claudioed.tasks.domain.CreateTaskRequest import tech.claudioed.tasks.domain.Task import tech.claudioed.tasks.domain.repository.TaskRepository import java.util.* /** * @author claudioed on 25/03/18. * Project tasks */ @Service class TaskService(private val taskRepository: TaskRepository) { fun tasks() = this.taskRepository.findAll() fun task(id: String) = this.taskRepository.findById(id) fun newTask(request: CreateTaskRequest) = this.taskRepository.save( request.let { Task(id = UUID.randomUUID().toString(), owner = request.owner, description = request.description, projectId = request.projectId) }) } |
There are a couple of interesting things here. Let’s start by injection, there is no necessity to use @Autowired in class constructor since Spring Framework 4 version, as we can see it will work as expected here as well. We use val in favor immutability.
Let’s understand the tasks() function. The function has no body because of the implementation has one line only. In this case, the function return can be omitted as well. It makes the code more concise and easy to understand.
We have used the same features in our other functions.
The full documentation about functions can be found here.
The REST Layer
Our REST Layer should be reactive. Then we need to return Flux or Mono in our methods. We will use the one line function and we can omit these declarations, keep in mind to achieve reactive functionalities we need Flux or Mono in our methods.
The TaskResource class should be like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
package tech.claudioed.tasks.domain.resource import org.springframework.web.bind.annotation.* import tech.claudioed.tasks.domain.CreateTaskRequest import tech.claudioed.tasks.domain.service.TaskService /** * @author claudioed on 25/03/18. * Project tasks */ @RestController @RequestMapping("/api/tasks") class TaskResource(private val taskService: TaskService) { @GetMapping("/{id}") fun task(@PathVariable("id") id:String) = this.taskService.task(id) @PostMapping fun newTask(@RequestBody request: CreateTaskRequest) = this.taskService.newTask(request) @GetMapping fun tasks() = this.taskService.tasks() } |
As we can see we are using the same as we did before, we have not used return methods declaration, the compiler can infer it for us, also it prevents developers errors as well.
We are using the @GetMapping and @PostMapping instead of @RequestMapping annotations, it makes our code more readable.
Configuring the MongoDB connections
We will use the yaml file, it makes our configuration file more readable and introduces semantics in our file. The configuration file should be like this:
1 2 3 4 5 6 7 8 9 |
spring: data: mongodb: host: localhost port: 27017 application: name: tasks-microservice server: port: 7070 |
There is nothing special here, a couple of configurations for mongoDB and tomcat server.
The Final Project Structure
Let’s analyze the final project structure, you can put in your preferred structure. I suggest the following one:
Excellent, now we can run it.
Run it and try your pretty new API using Kotlin Language!!!
Awesome Job, well done!!!
The full source code can be found here.
Tip
I recommend docker to run a mongoDB instance it makes your life extremely easy.
Book
You can find more detailed implementations and different Use Case in my book ( Spring 5.o By Example), published by Packt.
Thank you, for next Post, I will write about Spring Cloud Gateway and how it can help developers to work with Routes.
BYE.