Pourquoi (et comment) utiliser @ConfigurationProperties
Introduction
Le plus simple pour mapper
des propriétés issues d’un fichier application.properties (ou application.yml) reste d’utiliser l’annotation @Value(${ma.super.propriete})
:
|
|
C’est plutôt direct, et relativement bien intégré à Intellij (il nous interpole sa valeur et propose même de l’autocomplétion dans le @Value
si c’est un fichier .properties). Mais on pourrait aller plus loin. En premier lieu, aucune autocomplétion n’est disponible dans le fichier de propriétés. En second lieu, on ne peut pas réellement faire de validation de données sur le contenu renseigné dans ce fichier. On va donc voir un moyen d’aller un petit peu plus loin et de pallier à ces problèmes.
Autcomplétion et propriétés
Le principe de base
Pour proposer de l’autocomplétion dans les fichiers de properties
, IntelliJ se base sur un fichier additional-spring-configuration-metadata.json que l’on peut placer dans les ressources du projet.
Si on a les propriétés suivantes dans notre application.properties :
|
|
Il ressemble à ça :
|
|
Pour information, IntelliJ vous propose tout seul de générer ce fichier. Dans le fichier de propriétés, faites
alt + entrée
sur les propriétés qu’il ne connait pas.
On y retrouve donc nos deux propriétés et leur type associés. Une description peut-être fournie, pour mieux s’y retrouver dans ce fichier. Il est d’ailleurs dommage que cette dernière ne se retrouve pas dans le fichier de properties
via le raccourci pour afficher la doc (ctrl + q
sous IntelliJ).
Voici donc le principe de base. Cependant, l’injection se fait toujours via @Value("${ma.super.propriete}")
, ce qui n’est pas pratique en cas de refactoring
. Et le fichier de metadata est fastidieux à maintenir à la main. On va donc voir comment automatiser tout ça.
Les @ConfigurationProperties
Il existe une annotation @ConfigurationProperties
dans Spring Boot qui permet de gérer les propriétés sous forme d’une classe Java. En gros nos propriétés de tout à l’heure se retrouveraient sous cette forme :
|
|
Si d’aventure vous utilisez Lombok, ça rend ça encore plus lisible.
Notez la valeur dans l’annotation, qui définit le préfixe de l’annotation. Nos propriétés sont ainsi directement liées à cette classe Java et on peut facilement en lire les valeurs, sans utiliser @Value("${ma.super.propriete}")
.
Pour pouvoir utiliser cette classe, on peut l’annoter avec @Component
et ainsi l’injecter directement là où ça nous intéresse. On peut également éviter l’utilisation de @Component
en utilisant une @EnableConfigurationProperties(MaSuperConfig.class)
sur une classe configuration (on peut par exemple annoter notre @SpringBootApplication
avec), mais cette annotation doit prendre en paramètre l’ensemble des classes de propriétés. Et enfin, si on souhaite moins s’embêter, et déléguer à Spring le scan de toutes nos classes, il existe l’annotation @ConfigurationPropertiesScan
(disponible depuis Spring Boot 2.2). Elle est aussi à utiliser sur une classe de configuration.
Avec ça, nous avons simplifié l’accès à nos propriétés. Il nous faut cependant toujours gérer le additional-spring-configuration-metadata.json à la main.
spring-boot-configuration-processor
, ou comment Spring Boot nous sauve la mise
Il existe heureusement une dépendance Spring Boot qui va nous macher le travail :
|
|
Au prochain build du projet, un fichier spring-configuration-metadata.json sera automatiquement généré dans target/classes/META-INF/
:
|
|
Il ressemble dans les grandes lignes à ce que l’on avait avant. On a cependant en plus un attribut sourceType
qui référence notre classe Java de configuration.
Rajoutons dorénavant une petite documentation sur chacune des propriétés :
|
|
Faites un petit tour dans votre fichier de propriété, utilisez ctrl + q
sous IntelliJ et observez le résultat ! Sympa, non ?
Passons dorénavant à l’étape suivante, rajoutons un peu de contrôle sur ces propriétés.
Validation de données
Comme vous avez sûrement dû le remarquer, on a par défaut une validation liée au type des propriétés. Si vous essayez de placer une String dans un Integer, l’IDE va vous le faire remarquer et l’application ne démarrera pas :
|
|
Mais on peut affiner le contrôle, de la même façon qu’on validerait le body
d’un contrôleur REST. On va donc commencer par rajouter l’Api de validation Java et son implémentation de référence : Hibernate Validator. Comme souvent, il y a un starter
pour ça :
|
|
On a maintenant accès à plusieurs annotations intéressantes, dont voici un petit aperçu :
|
|
Après avoir renseigné les propriétés suivantes :
|
|
on a le message d’erreur suivant au démarrage de l’application :
|
|
Je voulais attirer votre attention sur l’annotation
@Validated
en haut du code Java. Sans cette annotation, la validation ne sera pas lancée !
On va maintenant pousser un peu plus loin notre réflexion.
Pour aller plus loin
Des classes de propriétés plus complexes
La première chose qui pourrait venir à l’esprit serait de pouvoir gérer des niveaux de propriétés imbriqués. Pour ne pas pouvoir utiliser maxds.general.ma-super-propriete
ou maxds.technique.spring.ma-super-propriete
? Eh bien, il y a deux façons de procéder.
La première possibilité consiste à utiliser une inner class dans la classe de propriétés. Ainsi, si on veut ajouter le niveau general
dans nos propriétés, on aurait la chose suivante :
|
|
On a ici gardé
maSuperPropriete
au premier niveau.
On est en présence de quelque chose qui fonctionne, mais qui peut vite devenir assez chargé, surtout si l’on a beaucoup de propriétés imbriquées (avec chacune les accesseurs). Personnellement, je préfère la seconde solution :
|
|
Et General.java :
|
|
Alors il y a deux éléments à remarquer ici. Tout d’abord, dans MaSuperConfig
, j’ai rajouté @Valid
et @NestedConfigurationProperty
. La première, permet de valider le contenu de attributs de General
(c’est le prolongement de ce qu’on a demandé avec @Validated
). La seconde, indique à Spring que cette classe doit être considérée comme une classe propriété à part entière.
Des propriétés immutables
Depuis le début de cet article, on se borne à rajouter des accesseurs et des modifiés sur nos propriétés. Sauf que vous avez sûrement remarqué qu’on ne souhaite pas modifier nos propriétés, seulement les lire. Essayez de supprimer les accesseurs et vous aurez :
|
|
Eh oui, il faut bien que le framework puisse valoriser vos classes au moment du démarrage de l’application ! Heureusement, depuis Spring Boot 2.2, il est possible d’y remédier en rajoutant un constructeur et l’annotation @ConstructorBinding
.
On va donc obtenir l’exemple suivant :
|
|
Conclusion
Dans cet article nous avons étudié un peu plus en profondeur l’utilisation de @ConfigurationProperties
et le confort que ça peut nous apporter lors de nos développements. Comme d’habitude un projet de démonstration est disponible à cette adresse.