Ma première application Spring Boot 2 avec Kotlin

3

Kotlin est un langage de programmation développé par JetBrains (IntelliJ IDEA) et proposé comme un remplaçant de Java. En tant que développeur Java / Spring depuis plusieurs années j’étais curieux de voir ce que le langage avait à offrir. Suite à la release officielle de Spring Boot 2 le 1er mars dernier j’ai décidé de sauter le pas et de créer ma première application web Kotlin.

Je vais créer l’application de gestion de livres kbooks avec les librairies suivantes :

  • Spring 5
  • Hibernate (avec JPA)
  • Jersey
  • H2

Le projet complet est disponible sur GitHub : https://github.com/Zenika/kbooks/tree/1.0.0

Prérequis

Si vous n’êtes pas familiers avec la syntaxe de Kotlin, je vous invite à suivre ce lien qui présente les bases du langage : https://medium.com/@magnus.chatt/why-you-should-totally-switch-to-kotlin-c7bbde9e10d5

Création du projet

La création de notre projet se fait comme en Java via http://start.spring.io/, il suffit de sélectionner Kotlin dans la liste des langages. Ensuite j’ajoute les dépendances dont j’ai besoin.

Modèle de données

Pour créer les entités, j’utilise les data class de Kotlin. Tous les paramètres doivent avoir une valeur par défaut pour que le compilateur Kotlin génère un constructeur vide, obligatoire avec Hibernate. À noter l’utilisation de la méthode mutableListOf() pour créer une liste mutable vide.

Book.kt, Author.kt :

Ensuite je crée les interfaces d’accès aux entités. Elles héritent de l’interface  PagingAndSortingRepository et leur implémentation est générée par Spring Data. Comme ces interfaces n’ont pas de contenu, je peux omettre les accolades.

IBookRepository.kt, IAuthorRepository.kt :

Service de gestion des livres

Maintenant que j’ai créé le modèle de données et les interfaces pour y accéder, je crée le service d’accès aux livres.

BookService.kt :

Ici j’utilise plusieurs spécificités de Kotlin :

  • Le mot clé lateinit qui permet l’injection de dépendance tout en ayant un type non null
  • it qui est le nom par défaut du paramètre d’une lambda lorsqu’il n’y en a qu’un
  • L’opérateur elvis ?: qui exécute l’opérande à sa droite si la valeur à gauche est nulle
  • L’interpolation de chaines de caractères. Dans « Book $id does not exist », $id sera remplacé par la valeur de la variable id.

Je crée également le dto BookDto, comme pour l’entité j’utilise une data class.

Enfin l’entité est convertie en dto par le singleton BookDtoConverter :

C’est le mot clé object qui définit BookDtoConverter comme un singleton. Son instance sera créée de manière “thread safe” par le runtime Kotlin, au moment du premier appel à la méthode convert.

La couche REST

Maintenant pour exposer le service via une api REST j’utilise Jax-RS avec Jersey.

BookResource.kt :

Comme la méthode getBook est très courte, je peux utiliser une notation simplifiée sans accolades.

Enfin, je crée une configuration Spring pour exposer la ressource :

JerseyConfig.kt :

Cette classe hérite de ResourceConfig et son constructeur par défaut appelle le constructeur vide de ResourceConfig. Nous enregistrons la ressource dans un bloc init car il sera appelé par tous les constructeurs de la classe (si j’en ajoute un).

Tester avec Mockito-Kotlin

Je réalise les tests avec JUnit, Mockito, kotlin-test et mockito-kotlin. Les deux dernières librairies sont des wrappers des deux premières qui facilitent l’écriture de tests unitaires en Kotlin.

BookServiceTest.kt :

L’écriture des tests unitaires est similaire à ceux que l’on écrit en Java mais encore une fois l’écriture est plus concise.

Conclusion

Le développement d’une application Spring Boot avec Kotlin est très proche de ce qui est fait en Java, on ne perd pas ses repères. Néanmoins ces quelques exemples mettent en avant les avantages de Kotlin qui est plus concis et permet d’être plus productif qu’avec Java.

Pour aller plus loin, je vous invite à découvrir les autres plateformes pour lesquelles Kotlin peut être utilisé. Vous pouvez par exemple faire du React mais également des applications natives pour Windows ou Android. Et si vous voulez tester Kotlin sans l’installer je vous invite à utiliser l’image Docker alpine-kotlin.

 

Partagez cet article.

A propos de l'auteur

3 commentaires

  1. Olivier Le Merdy on

    Merci pour cet article. Je suis fan de Kotlin et de ses data-classes. En revanche, j’avais des appréhensions sur le fait d’utiliser les data classes (et leur implémentation automatique de HashCode/Equals) pour implémenter des entités. En effet, les recommandations d’implémentation de ces méthodes pour des entités ne correspondent pas à ce comportement des data classes (cf. https://developer.jboss.org/wiki/EqualsandHashCode) et ce sans parler du comportement d’égalité entre collections proxifiées. Avec votre expérience d’un cas réel d’application, est-ce que cela pose problème?

    • Vincent Poilvert on

      Bonjour Olivier,

      Je n’ai pas encore eu la chance de mettre d’application Kotlin en prod mais je partage tes apprehensions. Plus généralement cela peut poser des problèmes lors de l’utilisation de Map/Set mais il existe une solution de contournement.

      Ci-dessous un extrait de la documentation officielle :

      If there are explicit implementations of equals(), hashCode() or toString() in the data class body or final implementations in a superclass, then these functions are not generated, and the existing implementations are used;

      De plus on peut appeler « super.hashCode() » pour appeler l’implémentation de la classe Any. Je conseille donc d’utiliser des data class en surchargeant ces méthodes si besoin est. Cela permet de profiter des implémentations automatiques de toString() et copy() mais également du destructuring.

Ajouter un commentaire