Getting started with Spring Boot and Kotlin

Who am I?

  • 🇧🇷🇵🇹 Software Developer
  • Working for 2 years in Germany

Lucas Felix de Sousa

Software Architect 

Agenda

  • Kotlin
    • Basics
    • Null Safety
    • Extensions
  • Spring Framework
    • Dependency Injection and IoC
  • Spring Boot
    • Configuration
    • Rest Controller
    • Error Handling
  • Demo

Kotlin

  • Created by JetBrains in 2011
  • Designed to be compatible with Java
// Function with default parameters
fun greet(name: String = "stranger") {
    // String interpolation
    println("Hello $name")
}

fun main() {
    greet("Coding Leipzig") // Hello Coding Leipzig
    greet() // Hello stranger
}
// Top level functions, functions without a class
fun main() = println("Hello, world!")

Kotlin

// var can be reassigned (get/set)
// val is readonly (get)
class Person(val name: String, var nationality: String = "unknown") {
    fun print() = println("Name: $name, Nationality: $nationality")
}

fun main() {
    val lucas = Person(name = "Lucas")
    lucas.print() // Name: Lucas, Nationality: unknown
    
    lucas.nationality = "BR"
    lucas.print() // Name: Lucas, Nationality: BR
    
    lucas.name = "some" // Compiler error, val cannot be reassigned
}

NullPointerException

private static void someMethod(String param) {
    System.out.println(param.length);
}

public static void main(String[] args) {
    someMethod("abc");
    someMethod(null);  // NullPointerException
}

Kotlin Null Safety

// '?' makes any class Nullable
fun someMethod(param: String?) {
    // '?.' null safe access to methods and properties
    println(param?.length)
}

fun main() {
    someMethod("abc")
    someMethod(null)  // Prints null
}
  • A common problem in the Java environment
  • Kotlin has different types for nullable and not-nullable objects
private static void someMethod(String param) {
    System.out.println(param.length);
}

public static void main(String[] args) {
    someMethod("abc");
    someMethod(null);  // NullPointerException
}
fun someMethod(param: String) {
    println(param.length)
}

fun main() {
    someMethod("abc")
    someMethod(null)  // Compiler error
}

Kotlin Null Safety - Elvis Operator

Better way to handle null values

fun hello(someone: String?) {
    // we can use it for applying a default value
    val name: String = someone ?: "guest"
    println("Hello $name")
}
fun getPerson(id: Long) : Person? {
    // return the person or null if doesn't exist
}

fun assertPersonExists(id: Long) {
    // we can also use it for throwing an exception when is null
    getPerson(id) ?: throw RuntimeException("Person with $id doesn't exist")
}

Kotlin Extensions

Extending class functionality

fun String.lastChar() {
  return this.get(this.lastIndex)
}

fun List<Any>.printAll() {
    for (s in this) {
        println(s)
    }
}

fun Int.sum(n : Int) : Int = this + n

fun main() {
    println("lucas".lastChar())

    listOf(1, 2, 3, 4, 5).printAll()
    listOf("a", "b", "c").printAll()

    println(1.sum(2))
}

Try Kotlin

Inversion of Control

Hollywood Principle: Don't call us, we'll call you

class MovieLister {
    var finder : MovieFinder = FileMovieFinder("movies.txt")
}
class MovieLister(val finder: MovieFinder)
  • Loose coupling
  • Easier to change implementation
  • Increase Testability using mocks
interface MovieFinder {
    fun findAll() : List<Movie>
}

class FileMovieFinder(val file: String): MovieFinder {
    override fun findAll(): List<Movie> {
        // list movies from file
    }
}

class IMDBMovieFinder(): MovieFinder {
    override fun findAll(): List<Movie> {
        // list movies from IMDB
    }
}

Spring Framework

  • Created in 2002
  • Dependency Injection
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/mydb"/>
    <property name="username" value="root"/>
    <property name="password" value="masterkaoli"/>
</bean>
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");

Spring Framework - Creating Beans

Better way to configure beans

@Configuration
class SpringConfig {
    @Bean
    fun movieLister(): MovieLister {
        return MovieLister(FileMovieFinder("movies.txt"))
    }
}
@Component
class IMDBMovieFinder(): MovieFinder {
    override fun findAll(): List<Movie> {
        // list movies from file
    }
}

Creates a bean manually

Creates a bean and inject dependencies automatically for this class

Configure Spring Beans manually

Spring Framework - Injecting dependencies

Declaring dependencies

@Component
class MovieLister {
    @Autowired
    var finder: MovieFinder?
}
@Component
class MovieLister(val finder: MovieFinder)

Inject dependency using setter

Inject dependency using constructor

The Spring team recommends constructor injection:

  • Immutable objects
  • Required dependencies are not null
  • Increase testability

Configuration Hell

  • Configure servlet.xml
  • Configure web.xml
  • Configure web server
  • Deploy WAR file

Spring Boot

  • Starts Tomcat embedded server
  • Automatically configure Spring and many 3rd dependencies
  • No XML configuration
  • Creates runnable JAR file
@SpringBootApplication
@RestController
class KotlinSpringBootApplication {
    @GetMapping("/")
    fun root() = "Hello World"
}

fun main() {
    runApplication<KotlinSpringBootApplication>()
}

Spring Boot Configuration

Add starter POM and configure

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
# Server HTTP port.
server.port=8080

Adding Dependendency

Configuring

pom.xml

application.properties

Spring Boot Configuration

Add starter POM and configure

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>
# JDBC URL of the database.
spring.datasource.url=
# Login username of the database.
spring.datasource.username=
# Login password of the database.
spring.datasource.password=
# Additional native properties for JPA
spring.jpa.properties.*=

Adding Dependendency

Configuring

pom.xml

application.properties

Try Spring Boot

Spring Web and Rest Controller

Creating a REST API

@RestController
@RequestMapping("/something")
class MyFirstRestApi(private val service: MyService) {
    @GetMapping
    fun list(@RequestParam param1) {
        // doSomething
    }
}

Spring will manage this class and inject the dependencies

GET /something

Injected by Spring

Adds query parameter to variable param1
GET /something?param1=value

Spring Web and Rest Controller

Creating a REST API

@RestController
@RequestMapping("/something")
class MyFirstRestApi(private val service: MyService) {

    @GetMapping("/{id}")
    fun get(@PathVariable id: Long) {
        // Do something
    }

    @DeleteMapping("/{id}")
    @ResponseStatus(NO_CONTENT)
    fun delete(@PathVariable id: Long) {
        // Do something
    }
}

GET /something/{id}

DELETE /something/{id}

Default HTTP status is OK, this annotation can change it

Add value of {id} to the variable

Spring Web and Rest Controller

Creating a REST API

@RestController
@RequestMapping("/something")
class UserController(private val service: MyService) {
    @PostMapping
    @ResponseStatus(CREATED)
    fun add(@RequestBody @Valid someData: AddData) {
        // Do something
    }
}

POST /something

This method will receive a body and convert it to AddData class

Error Handling with Spring

To create a generic error handling for Exceptions is also an easy task

class CustomError(val message: String?)

@RestControllerAdvice
class CustomExceptionHandler {
    @ExceptionHandler(ResourceNotFoundException::class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    fun notFound(exception: ResourceNotFoundException) {
        return CustomError(exception.message)
    }
}

Demo

Next steps:

To know more:

Questions?

Thank you :)

Getting started with Spring Boot and Kotlin

By Lucas Felix

Getting started with Spring Boot and Kotlin

  • 457