Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Kotlin Microservice With Spring Boot Tutorial

DZone's Guide to

Kotlin Microservice With Spring Boot Tutorial

We walk through building a simple microservice using Kotlin and Spring Boot. It's the new hotness!

· Microservices Zone ·
Free Resource

Record growth in microservices is disrupting the operational landscape. Read the Global Microservices Trends report to learn more.

You may find many examples of microservices built with Spring Boot on my blog, but most of them are written in Java. With the rise in popularity of the Kotlin language, it is more often used with Spring Boot for building backend services. Starting with version 5, the Spring framework introduced first-class support for Kotlin. In this article, I’m going to show you an example of microservices built with Kotlin and Spring Boot 2. I’ll describe some interesting features of Spring Boot, which can be treated as a set of good practices when building backend, REST-based microservices.

1. Configuration and Dependencies

To use Kotlin in your Maven project you have to include plugin kotlin-maven-plugin, and /src/main/kotlin/src/test/kotlin directories to the build configuration. We will also set -Xjsr305 compiler flag to strict. This option is responsible for checking support for JSR-305 annotations (for example @NotNull annotation).

<build>
    <sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
    <testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
    <plugins>
        <plugin>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-maven-plugin</artifactId>
            <configuration>
                <args>
                    <arg>-Xjsr305=strict</arg>
                </args>
                <compilerPlugins>
                    <plugin>spring</plugin>
                </compilerPlugins>
            </configuration>
            <dependencies>
                <dependency>
                    <groupId>org.jetbrains.kotlin</groupId>
                    <artifactId>kotlin-maven-allopen</artifactId>
                    <version>${kotlin.version}</version>
                </dependency>
            </dependencies>
        </plugin>
    </plugins>
</build>

We should also include some core Kotlin libraries like kotlin-stdlib-jdk8and kotlin-reflect. They are provided by default for a Kotlin project on start.spring.io. For REST-based applications, you will also need the Jackson library for JSON serialization/deserialization. Of course, we have to include Spring starters for web applications together with Actuator, which is responsible for providing management endpoints.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.module</groupId>
    <artifactId>jackson-module-kotlin</artifactId>
</dependency>
<dependency>
    <groupId>org.jetbrains.kotlin</groupId>
    <artifactId>kotlin-reflect</artifactId>
</dependency>
<dependency>
    <groupId>org.jetbrains.kotlin</groupId>
    <artifactId>kotlin-stdlib-jdk8</artifactId>
</dependency>

We use the latest stable version of Spring Boot with Kotlin 1.2.71

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.2.RELEASE</version>
</parent>
<properties>
    <java.version>1.8</java.version>
    <kotlin.version>1.2.71</kotlin.version>
</properties>

2. Building the Application

Let’s begin with the basics. If you are familiar with Spring Boot and Java, the biggest difference is in the main class declaration. You will call runApplication method outside the Spring Boot application class. The main class, the same as in Java, is annotated with @SpringBootApplication.

@SpringBootApplication
class SampleSpringKotlinMicroserviceApplication

fun main(args: Array<String>) {
    runApplication<SampleSpringKotlinMicroserviceApplication>(*args)
}

Our sample application is very simple. It exposes some REST endpoints providing CRUD operations for the model object. Even with this fragment of code, illustrating controller implementation, you can see some nice Kotlin features. We may use a shortened function declaration with an inferred return type. The annotation @PathVariable does not require any arguments. The input parameter name is considered to be the same as the variable name. Of course, we are using the same annotations as with Java. In Kotlin, every property declared as having non-null type must be initialized in the constructor. So, if you are initializing it using dependency injection it has to declared as lateinit. Here’s the implementation of PersonController.

@RestController
@RequestMapping("/persons")
class PersonController {

    @Autowired
    lateinit var repository: PersonRepository

    @GetMapping("/{id}")
    fun findById(@PathVariable id: Int): Person? = repository.findById(id)

    @GetMapping
    fun findAll(): List<Person> = repository.findAll()

    @PostMapping
    fun add(@RequestBody person: Person): Person = repository.save(person)

    @PutMapping
    fun update(@RequestBody person: Person): Person = repository.update(person)

    @DeleteMapping("/{id}")
    fun remove(@PathVariable id: Int): Boolean = repository.removeById(id)

}

Kotlin automatically generates getters and setters for class properties declared as var. Also if you declare the model as a data class it generates equalshashCode, and toString methods. The declaration of our model class Personis very concise as shown below.

data class Person(var id: Int, var name: String, var age: Int, var gender: Gender)

I have implemented my own in-memory repository class. I use Kotlin extensions for manipulating the list of elements. This built-in Kotlin feature is similar to Java streams, with the difference that you don’t have to perform any conversion between Collection and Stream.

@Repository
class PersonRepository {
    val persons: MutableList<Person> = ArrayList()

    fun findById(id: Int): Person? {
        return persons.singleOrNull { it.id == id }
    }

    fun findAll(): List<Person> {
        return persons
    }

    fun save(person: Person): Person {
        person.id = persons.size + 1
        persons.add(person)
        return person
    }

    fun update(person: Person): Person {
        val index = persons.indexOf(person)
        persons[index] = person
        return person
    }

    fun removeById(id: Int): Boolean {
        return persons.removeIf { it.id == id }
    }

}

The sample application source code is available on the GitHub in repository, here.

3. Enabling Actuator Endpoints

Since we have already included Spring Boot starter with Actuator in the application code, we can take advantage of its production-ready features. Spring Boot Actuator gives you very powerful tools for monitoring and managing your apps. You can provide advanced health checks, info endpoints, or send metrics to numerous monitoring systems like InfluxDB. After including Actuator artifacts, the only thing we have to do is to enable all its endpoint for our application via HTTP.

management.endpoints.web.exposure.include: '*' 

We can customize Actuator endpoints to provide more details about our app. A good practice is to expose information about the version and git commit to the info endpoint. As usual, Spring Boot provides auto-configuration for such features, so the only thing we need to do is to include some Maven plugins to the build configuration in pom.xml. The goal, build-info, is set for spring-boot-maven-plugin and forces it to generate a properties file with basic information about the version. The file is located in the directory, META-INF/build-info.properties. The plugin git-commit-id-plugin will generate a git.propertiesfile in the root directory.

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <executions>
        <execution>
            <goals>
                <goal>build-info</goal>
            </goals>
        </execution>
    </executions>
</plugin>
<plugin>
    <groupId>pl.project13.maven</groupId>
    <artifactId>git-commit-id-plugin</artifactId>
    <configuration>
        <failOnNoGitDirectory>false</failOnNoGitDirectory>
    </configuration>
</plugin>

Now you should just build your application using mvn clean installcommand and then run it.

$ java -jar target\sample-spring-kotlin-microservice-1.0-SNAPSHOT.jar 

The info endpoint is available under address http://localhost:8080/actuator/info. It exposes some interesting information for us.

{
    "git":{
        "commit":{
            "time":"2019-01-14T16:20:31Z",
            "id":"f7cb437"
        },
        "branch":"master"
    },
    "build":{
        "version":"1.0-SNAPSHOT",
        "artifact":"sample-spring-kotlin-microservice",
        "name":"sample-spring-kotlin-microservice",
        "group":"pl.piomin.services",
        "time":"2019-01-15T09:18:48.836Z"
    }
}

4. Enabling API Documentation

Build info and git properties may be easily injected into the application code. It can be useful in some cases. One of those cases is if you have enabled auto-generated API documentation. The most popular tools for this is Swagger. You can easily integrate Swagger 2 with Spring Boot using the SpringFox Swagger project. First, you need to include the following dependencies to your pom.xml.

<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>2.9.2</version>
</dependency>
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>2.9.2</version>
</dependency>

Then, you should enable Swagger by annotating the configuration class with @EnableSwagger2. The required information is available inside the beans, BuildProperties and GitProperties. We just have to inject them into the Swagger configuration class as shown below. We set them as optional to prevent application startup failure in case they are not present on the class path.

@Configuration
@EnableSwagger2
class SwaggerConfig {

    @Autowired
    lateinit var build: Optional<BuildProperties>
    @Autowired
    lateinit var git: Optional<GitProperties>

    @Bean
    fun api(): Docket {
        var version = "1.0"
        if (build.isPresent && git.isPresent) {
            var buildInfo = build.get()
            var gitInfo = git.get()
            version = "${buildInfo.version}-${gitInfo.shortCommitId}-${gitInfo.branch}"
        }
        return Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo(version))
                .select()
                .apis(RequestHandlerSelectors.any())
                .paths{ it.equals("/persons")}
                .build()
                .useDefaultResponseMessages(false)
                .forCodeGeneration(true)
    }

    @Bean
    fun uiConfig(): UiConfiguration {
        return UiConfiguration(java.lang.Boolean.TRUE, java.lang.Boolean.FALSE, 1, 1, ModelRendering.MODEL, java.lang.Boolean.FALSE, DocExpansion.LIST, java.lang.Boolean.FALSE, null, OperationsSorter.ALPHA, java.lang.Boolean.FALSE, TagsSorter.ALPHA, UiConfiguration.Constants.DEFAULT_SUBMIT_METHODS, null)
    }

    private fun apiInfo(version: String): ApiInfo {
        return ApiInfoBuilder()
                .title("API - Person Service")
                .description("Persons Management")
                .version(version)
                .build()
    }

}

The documentation is available under the context path,/swagger-ui.html. Besides API documentation, it displays the information about the application version, git commit id, and branch name.

Image title

5. Choosing Your App Server

Spring Boot Web can be run on three different embedded servers: Tomcat, Jetty, or Undertow. By default, it uses Tomcat. To change the default server, you just need to include the suitable Spring Boot starter and exclude spring-boot-starter-tomcat. A good practice may be to enable switching between servers during application build. You can achieve it by declaring Maven profiles as shown below.

<profiles>
    <profile>
        <id>tomcat</id>
        <activation>
            <activeByDefault>true</activeByDefault>
        </activation>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
        </dependencies>
    </profile>
    <profile>
        <id>jetty</id>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
                <exclusions>
                    <exclusion>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-starter-tomcat</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-jetty</artifactId>
            </dependency>
        </dependencies>
    </profile>
    <profile>
        <id>undertow</id>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
                <exclusions>
                    <exclusion>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-starter-tomcat</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-undertow</artifactId>
            </dependency>
        </dependencies>
    </profile>
</profiles>

Now, if you would like to enable servers other than Tomcat for your application, you should activate the appropriate profile during the Maven build.

$ mvn clean install -Pjetty 

Conclusion

The development of microservices using Kotlin and Spring Boot is nice and simple. Based on this sample application, I have introduced the main Spring Boot features for Kotlin. I also described some good practices you may apply to your microservices when building them using Spring Boot and Kotlin. You can compare the described approach with some other micro-frameworks used with Kotlin, for example, Ktor, described in one of my previous articles: Kotlin Microservices with Ktor.

Learn why microservices are breaking traditional APM tools that were built for monoliths.

Topics:
microservices ,spring boot ,kotlin tutorial ,spring boot tutorial ,microservices tutorial

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}