Spring Boot, le couteau suisse du développeur Java

Ralenti et frustré par les problèmes de build rencontrés en utilisant Play Framework (que j’avais détaillés ici), j’ai finalement décidé de migrer vers Spring Boot.

Après quelques mois (ou années pour certains morceaux) de développement et d’exploitation d’applications Spring Boot en production, je pense avoir pu prendre le recul nécessaire pour me livrer à l’exercice du retour d’expérience.

De sérieux atouts

Une prise en main simple

La documentation propose un ensemble de guides expliquant pas à pas comment répondre à un cas d’utilisation donné avec Spring Boot.

Le principe est toujours le même, quoi qu’on veuille faire avec Spring Boot. On applique le plugin Spring Boot à son système de build (Maven ou Gradle) et on crée une classe contenant la méthode main de l’application (point d’entrée). On délègue le démarrage à Spring Boot :

package hello;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

La suite dépend de ce qu’on souhaite faire. Suivons par exemple le guide expliquant comment faire un web service REST avec Spring Boot. Il faut rajouter la dépendance au starter web de Spring Boot pour activer la partie web. Il ne reste plus qu’à créer un contrôleur pour le web service, et le tour est joué :

package hello;

import java.util.concurrent.atomic.AtomicLong;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class GreetingController {

    private static final String template = "Hello, %s!";
    private final AtomicLong counter = new AtomicLong();

    @RequestMapping("/greeting")
    public Greeting greeting(@RequestParam(value="name", defaultValue="World") String name) {
        return new Greeting(counter.incrementAndGet(),
                            String.format(template, name));
    }
}

Au démarrage, Spring Boot découvre automatiquement l’ensemble des composants qu’il a à gérer. Ici, il va tomber sur l’annotation @RestController qui lui indique que cette classe est un contrôleur. @RequestMapping indique le chemin du web service et la méthode (GET par défaut).  Ce web service prend un paramètre (query string) name qui a une valeur par défaut World. Lorsqu’il est appelé, ce web service renvoie l’objet de type Greeting représenté automatiquement en JSON. Simple et efficace !

Du Java standard

Spring Boot s’appuie sur des fonctionnalités standard de Java, notamment en ce qui concerne le build. On peut utiliser les mêmes outils (IDE, outil de build) pour coder en Spring Boot que ceux qu’on utilise habituellement en Java. Contrairement à Play Framework, Spring Boot n’est pas lié à un outil de build. Ceci dit, Spring Boot vient avec un plugin Maven ou Gradle pour enrichir et simplifier le build.

Un écosystème riche et cohérent

L’écosystème Spring existe depuis une quinzaine d’années et s’est enrichi au fur et à mesure. Il permet aujourd’hui de répondre à la plupart des problématiques qui se posent aux développeurs d’applications modernes. On peut par exemple faire des sites web, des web services, gérer la sécurité ou la configuration de son application, se connecter à différentes bases de données, envoyer des emails et bien d’autres choses.

Spring Boot se positionne comme le chef d’orchestre de ces différents projets. Il suffit de lui indiquer lesquels on souhaite utiliser via une dépendance sur les starters associés et il se charge de les rapatrier, les intégrer et les configurer.  Les différents projets Spring n’ont pas forcément tous le même cycle de vie. Spring Boot s’assure de fournir des versions cohérentes et compatibles entre elles.

Des fonctionnalités avancées

Spring Boot répond généralement de manière simple et élégante aux cas d’utilisation classiques et basiques. Mais quand la problématique se corse, il a du répondant et sait nous accompagner jusque dans des cas d’utilisation avancés. J’ai jusqu’à présent réussi à répondre à tous les problèmes auxquels j’ai été confronté en restant dans le cadre proposé par l’outil, sans devoir mettre en place des contournements sauvages comme il m’est arrivé de devoir le faire dans le passé avec d’autres outils.

Ce n’est en fin de compte pas très étonnant parce que Spring Boot n’est finalement que la réunion de l’ensemble des outils Spring répondant chacun à une problématique donnée. Tous ces projets peuvent être utilisés de manière indépendante et abordent leur thématique en profondeur.

Dans un autre domaine, la programmation réactive est à la mode ces temps-ci. Ça tombe bien parce que Spring Boot 2.0 (sorti le mois dernier) permet de choisir son paradigme de programmation : bloquant (historique) ou non bloquant (nouveau, basé sur Reactor). L’ensemble de l’écosystème Spring (du sol au plafond) propose ainsi des API bloquantes ou non bloquantes : Spring Data, Spring Security, Spring MVC… On peut maintenant construire une application intégralement asynchrone avec Spring Boot 2.

De la même façon, Spring Boot 2 est compatible avec Kotlin, un langage qui a le vent en poupe en ce moment.

Prêt à partir en production

Bien que Spring Boot ne soit pas dépendant d’un outil de build, il vient avec un plugin pour simplifier son utilisation dans Maven ou Gradle. Ce plugin permet, entre autres, de construire un jar autonome qu’il suffit de lancer avec la commande java -jar hello.jar.

Le mécanisme de gestion de configuration propose de multiples façons de gérer la configuration de l’application. Il est ainsi très simple de définir les propriétés spécifiques à l’environnement dans lequel l’application est déployée via des options dans la ligne de commande ou un fichier YAML par exemple.

Le déploiement d’une application Spring Boot se résume donc à déployer le fichier jar sur la machine cible, de définir la configuration éventuelle et de créer un service qui démarre ce fichier jar.

Une fois l’application déployée en production, Spring Boot Actuator permet d’obtenir tout un tas d’informations concernant le fonctionnement de l’application et d’interagir avec elle, tout cela à travers des web services. On peut ainsi changer la configuration du logger, consulter tout un tas de métriques ou encore vérifier son état de santé (health check) via des indicateurs standard (base de données) par exemple ou des indicateurs qu’on écrit soi-même. C’est très facile à mettre en place et ça simplifie grandement le monitoring de l’application.

Une grande communauté

Spring Boot est un outil très populaire et assez répandu de nos jours. Son implantation sur le marché est un gage de pérennité sur le long terme, si tant est que sa pérennité soit encore à prouver parce que Spring existe depuis 15 ans et qu’il a su s’adapter à l’évolution des pratiques et des besoins. C’est aussi un atout pour le recrutement, c’est sans doute plus facile de recruter un développeur qui maîtrise Spring Boot qu’un développeur qui est à l’aise sur un framework plus exotique.

L’importante communauté de développeurs permet de trouver de la documentation ou de l’aide facilement. Il existe par exemple beaucoup de contenu sur Stack Overflow et on trouve souvent très vite la solution. Le site Baeldung propose également un très large éventail de tutoriels de très grande qualité.

Avec quelques défauts malgré tout

Un démarrage un peu lent

En passant de Play Framework à Spring Boot, j’ai gagné sur pas mal de points mais j’ai clairement perdu sur le plan du temps de démarrage de l’application. Une application Play Framework démarre en quelques secondes, une application Spring Boot avec un peu de code, c’est plutôt quelques dizaines de secondes. Cela vient du fait qu’au démarrage, Spring détecte l’ensemble des modules et des composants de l’application (les contrôleurs web par exemple). Le démarrage dans Play Framework est rapide parce que cette détection est faite au moment du build via de la génération. Si Play Framework démarre rapidement, son build est beaucoup plus complexe comme je l’avais expliqué dans mon retour d’expérience sur Play Framework et je crois que je préfère attendre un petit peu au démarrage…

Si cette latence au démarrage n’est pas très gênante en production, elle est plus pénalisante en mode développement, bien que les Developer Tools permettent de recharger le code à chaud sans redémarrer le serveur et gagner ainsi quelques précieuses secondes. L’exécution des tests n’est, elle non plus, pas épargnée par ce problème. Les tests de contrôleur par exemple ont besoin de démarrer le serveur. Pour limiter ce problème, le système d’exécution des tests réutilise l’instance du serveur précédemment lancée et permet de ne payer ce temps qu’une seule fois.

Difficile de trouver son chemin

L’écosystème Spring est composé de différentes briques répondant chacune à une problématique précise. De nombreux guides permettent de se familiariser avec chacune d’entre elles en nous accompagnant pas à pas vers la solution à un cas d’utilisation simple.

Mais quand on souhaite rentrer un peu plus en profondeur dans le sujet, c’est plus compliqué. La documentation de Spring Security par exemple est un énorme pavé qui décrit très bien ce que Spring Security sait faire. Quand on sait ce qu’on cherche, on trouve la solution assez vite. Mais c’est plus compliqué quand on ne sait pas trop ce qu’on cherche ou qu’on n’a pas encore bien compris le fonctionnement global de l’outil.

Heureusement, dans ces cas-là une recherche sur internet permet généralement assez vite de trouver la réponse via StackOverflow ou Baeldung par exemple.

Une adhérence forte

Spring Boot s’apparente davantage à un framework structurant qu’à une simple bibliothèque de fonctions. Il influence donc forcément la façon dont on code et la plupart des classes ont une dépendance sur lui, ne serait-ce que pour des annotations. Le code a ainsi une dépendance forte vis-à-vis de Spring. Est-ce un problème ? Pas forcément, il faut bien faire des choix et ne pas réinventer la roue à chaque fois. Mais ça pourrait le devenir si vous décidez de migrer votre code à un autre outil.

Ceci dit, même si la dépendance à Spring est omniprésente, elle n’est pas monolithique. Le code de la base de données a une dépendance sur Spring Data et Spring Core, celui des web services dépend, lui, de Spring MVC et Spring Core. On pourrait donc imaginer migrer le code web vers un autre outil et conserver Spring Data pour accéder à la base de données.

Héritage et immuabilité

Dans le code que j’écris, j’évite autant que possible d’éviter l’héritage. Malheureusement, quand on veut configurer ou personnaliser le comportement de Spring, on doit parfois implémenter des classes abstraites avec plusieurs niveaux d’héritage, je pense par exemple à OncePerRequestFilter. J’aurais préféré passer par le design pattern Strategy pour une meilleure testabilité notamment, mais ça fait quand même le boulot.

De la même façon, j’utilise de manière quasi-systématique l’immuabilité. Historiquement, Spring utilise un constructeur vide et des setters pour remplir les objets qu’il crée. Dans certains cas de figure, il sait utiliser un constructeur avec l’ensemble des champs de l’objet dans les paramètres, mais quand ce n’est pas le cas, il est impossible d’utiliser des objets immuables. C’est possible pour les beans ou dans Spring Data mais pas dans la configuration (@ConfigurationProperties, ça devrait cependant arriver avec Spring Boot 2.1).

Conclusion

Aucun outil n’est parfait, mais, vous l’aurez compris, Spring Boot a clairement ma préférence dans le monde des frameworks web modernes. Il propose des réponses simples et efficaces à une très grande majorité des problématiques auxquelles les développeurs d’applications modernes sont confrontés.

Là où la très grande majorité des frameworks web se contentent de gérer la partie web, Spring nous propose tout un tas de briques permettant de gérer les différents pans d’un application. Ces différentes briques s’intègrent parfaitement entre elles, éliminant ainsi des problèmes de compatibilité ou de jar hell par exemple.

Ce n’est pas tellement la qualité intrinsèque de Spring MVC qui fait que Spring se distingue des autres framework web, mais c’est la richesse de son écosystème qui fait la différence. Notons que j’ai fait le choix de comparer Spring Boot aux autres frameworks web, mais on peut très bien l’utiliser pour faire des applications d’autres types en s’appuyant sur d’autres projet de la galaxie Spring.

3 réflexions au sujet de « Spring Boot, le couteau suisse du développeur Java »

  1. Bel article! On utilise Spring Boot depuis bientôt 2ans sans accroc. On a migré récemment sous Spring Boot 2. L’écosystème Spring est vraiment riche, on gagne un temps considérable en dev avec toutes les libs disponibles.

    1. Merci Jérôme !

      J’ai également migré à Spring Boot 2 récemment. J’ai eu quelques modifications à faire mais ça ne m’a pas pris très longtemps. Et pour rester dans le comparatif avec Play Framework, c’était rien par rapport au passage de Play 2.n à 2.(n+1). Ca a été compliqué chez vous de migrer ?

      Bonne continuation !

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *