Primeros pasos con Spring y Kotlin

Ahora ya podemos crear nuestros proyectos Spring Boot y escoger como lenguaje Kotlin.

Con Maven



Las dependencias seran:



Se puede especificar la ubicación de tu código fuente y tus tests:


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

Para compilar módulos kotlin, código fuente se usa un plugin de maven para kotlin:



La clase DemoApplication es la que inicia todo, tiene la anotación @SpringBootApplication. Por defecto en Kotlin todas las clases son públicas y se puede declarar sin {}.


@SpringBootApplication
class DemoApplication

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


Como se puede observar, se omite el public porque es dado por defecto, si no hay tipo de retorno se omite el void, asimismo, como se declara la función fuera del cuerpo de la clase por defecto es static.

Si ejecutamos la aplicación dentro del IDE o con mvn spring-boot:run la aplicación iniciará en el puerto 8080.


El HelloController, lucirá así:


@RestController
class HelloController (val helloService: HelloService) {

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

    @GetMapping("/hello-service")
    fun helloKotlinService(): String {
        return helloService.getHello()
    }

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

Como se ve se tiene un constructor donde se dará la inyección sin usar @Autowired y con val para que sea inmutable. Tenemos 3 casos. El primero devuelve un String, el segunDo también pero usando un Service y el tercer caso devuelve un DTO. El service sería de esta manera:


@Service
class HelloService{

    fun getHello(): String {
        return "hello service"
    }
}

El DTO sería un data class:



data class HelloDto(val greeting: String)

Este tipo de data class automáticamente crea equals/hashcode, toString, y una función copy. Pero, no nos da un constructor por defecto, el cual es requerido por librerías como Jackson. Si se quiere soportar esto se puede usar esta nueva dependencia en tu pom.


Finalmente el testing sería de la siguiente manera:


@RunWith(SpringRunner::class)
@SpringBootTest(classes = arrayOf(DemoApplication::class),
  webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class DemoApplicationTests {

 @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")
 }

 @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")
 }


 @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"))
 }
}


De esto último se puede rescatar el null safety "?". Cuando una variable puede ser null debe ser declarada usando '?'.

Con Gradle

 Escogemos para esta otra versión la opción con Gradle.






A continuación veamos el build.gradle, los plugins y dependencias a usar.


buildscript {
 ext {
  kotlinVersion = '1.1.3-2'
  springBootVersion = '1.5.6.RELEASE'
 }
 repositories {
  mavenCentral()
 }
 dependencies {
  classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
  classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}")
  classpath("org.jetbrains.kotlin:kotlin-allopen:${kotlinVersion}")
 }
}

apply plugin: 'kotlin'
apply plugin: 'kotlin-spring'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'

version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8
compileKotlin {
 kotlinOptions.jvmTarget = "1.8"
}
compileTestKotlin {
 kotlinOptions.jvmTarget = "1.8"
}

repositories {
 mavenCentral()
}


dependencies {

 compile('org.springframework.boot:spring-boot-starter-web')
 compile("org.jetbrains.kotlin:kotlin-stdlib-jre8:${kotlinVersion}")
 compile("org.jetbrains.kotlin:kotlin-reflect:${kotlinVersion}")
 testCompile('org.springframework.boot:spring-boot-starter-test')
}



Luego todo lo explicado anteriormente aplica. Así que para tener una pequeña demo creamos esta clase Greeting y luego su Controller. Greeting.kt


package com.example.demogradle

/**
 * Created by josediaz on 7/27/17.
 */


data class Greeting(val id: Long, val content: String)



Y su controller:


@RestController
class GreetingController {

    val counter = AtomicLong()

    @GetMapping("/greeting")
    fun greeting(@RequestParam(value = "name", defaultValue = "World") name: String) =
            Greeting(counter.incrementAndGet(), "Hello, $name")

}


El resultado final es:
Enjoy! y los animo a empezar a utilizar kotlin en sus proyectos.

SOURCE CODE

Comentarios

Entradas populares