Le conteneur Spring
Introduction
Dans ce chapitre, nous abordons une approche simplifiée de Spring pour offrir une vue d’ensemble sans nous perdre dans trop de détails. Par la suite, nous approfondirons certains aspects essentiels pour une meilleure compréhension dans des applications plus complexes. L’exemple présenté ici couvre déjà de nombreuses questions liées à l’utilisation du framework.
Origines
Nous allons explorer les composants essentiels du framework Spring, qui simplifie la programmation. Il se compose principalement de Spring Core, qui gère les instances de classes en mémoire, ainsi que de bibliothèques de classes utilisant ce cœur, appelées des « beans Spring ». Ces beans sont extensibles et facilement utilisables.
Le cœur du framework charge automatiquement un ensemble de beans au démarrage et permet un accès simple en injectant automatiquement leur référence dans les objets qui les utilisent. Spring offre un contrôle précis sur la gestion des objets en mémoire.
Spring s’intègre harmonieusement avec de nombreux frameworks et produits. Son principal avantage réside dans la création et la mise à disposition automatisée de beans. Ces objets peuvent être de deux types : des singletons partagés ou des objets « prototypes » instanciés à chaque injection.
Les singletons sont instanciés une seule fois dans le conteneur Spring et partagés. Spring gère une liste interne des singletons, les instanciant au besoin et les injectant automatiquement. Les singletons sont généralement créés au démarrage de l’application, sauf quelques exceptions.
Les objets prototypes sont instanciés...
Modules fondamentaux
Les beans spécialisés dont nous allons discuter ont fait leur apparition dans Spring dès la version 0.9, et au fil des versions ultérieures, ils ont bénéficié d’améliorations constantes. Ces beans sont principalement utilisés lorsque nous cherchons à étendre les fonctionnalités de Spring, et ils se manifestent plus fréquemment dans les personnalisations ou extensions à travers un framework maison. Lorsque nous utilisons des annotations dans Spring, nous n’avons généralement pas à interagir directement avec ces objets spécialisés, car ils fonctionnent en coulisses pour prendre en charge diverses opérations internes au sein du framework Spring.
Nous plongerons plus en profondeur dans le fonctionnement des beans dans le chapitre Programmation orientée aspect avec Spring. Pour simplifier, pensez à un bean comme à un proxy qui enrichit un objet Java existant. Ce proxy offre plusieurs avantages, notamment :
-
Il permet de détourner les appels aux méthodes de l’objet pour y ajouter des comportements spécifiques.
-
Il autorise l’ajout de nouvelles méthodes à l’objet.
-
Il gère les liens vers d’autres objets référencés dans l’objet principal.
-
Il offre la possibilité de configurer les propriétés de l’objet de diverses manières, telles que des chaînes de caractères ou des fichiers via une factory.
-
Il gère la gestion de messages dans des bundles à travers le contexte.
-
Il prend en charge les événements interobjets, tels que la création, la destruction, et les événements utilisateurs.
Un bean peut être orienté soit vers les données (POJO) soit vers les traitements, en essayant de séparer ces aspects en beans spécialisés. Un bean contient donc plusieurs éléments, notamment la classe d’implémentation réelle, des éléments de configuration comportementale tels que singleton ou prototype, les beans liés pour l’injection de dépendances, et des valeurs de propriété à définir lors de la construction.
Lorsque nous souhaitons utiliser un bean Spring, nous...
Configuration des beans
La configuration des beans dans Spring peut être implicite ou explicite.
-
Mode implicite : dans ce mode, Spring est capable de reconnaître automatiquement quelles classes Java doivent être ajoutées à la liste des beans, en se basant sur des critères de balisage spécifiques appliqués aux classes candidates. Cette approche permet de configurer des beans sans avoir à spécifier explicitement chaque détail. Spring se charge de découvrir les classes appropriées et de les gérer en tant que beans. Bien que plus pratique, ce mode peut potentiellement comporter des risques si les règles de balisage ne sont pas correctement définies.
-
Mode explicite : contrairement au mode implicite, le mode explicite nécessite que nous indiquions précisément à Spring quels sont les noms et emplacements des beans que nous voulons configurer. Cela offre un niveau de sécurité plus élevé, car chaque bean doit être explicitement déclaré. Cependant, cela peut être plus fastidieux à configurer, car nous devons fournir des informations détaillées pour chaque bean. Ce mode est moins courant, car il implique davantage de travail manuel.
En ce qui concerne la configuration des beans, Spring offre plusieurs approches. Vous pouvez les configurer à l’aide de fichiers XML, d’annotations ou même directement en Java, en fonction de vos préférences et de votre projet. Chaque méthode a ses avantages et inconvénients, et le choix dépend souvent des besoins spécifiques du projet et de la préférence de l’équipe de développement.
Nous verrons en détail les différentes façons de configurer l’application.
Possibilité de déclaration des beans
Type |
Mode |
Utilisation |
Fichiers XML |
Explicite |
Beans personnalisés de façon externe pour une variation par environnement |
Code Java |
Implicite et explicite |
Beans de configurations statiques |
Annotations |
Implicite |
Beans subissant peu de changements structurels |
Lambda |
Implicite |
Sans proxy (nous détaillerons cet aspect en détail) |
Grâce à la configuration de Spring, nous avons la possibilité de déclarer une structure arborescente d’objets...
Contrôle du cycle de vie : construction et destruction
Dans Spring Framework, la gestion du cycle de vie des beans, notamment leur construction et leur destruction, peut être contrôlée et personnalisée grâce à divers mécanismes. Il est important de comprendre que les beans injectés ne peuvent pas être utilisés directement dans le constructeur d’un bean, car à ce moment-là, Spring n’a pas encore créé et injecté les dépendances. De même, l’utilisation de méthodes de destruction personnalisées est compliquée par les changements récents dans Java, notamment la dépréciation du bloc finally dans le contexte de la gestion des ressources (JEP 421).
Spring offre des annotations spécifiques pour marquer des méthodes à appeler lors de la création et de la destruction d’un bean :
@PostConstruct :
Cette annotation est utilisée sur une méthode pour indiquer qu’elle doit être appelée immédiatement après que le bean a été construit et que toutes les dépendances ont été injectées. Cela permet d’effectuer une initialisation personnalisée.
Exemple :
@PostConstruct
public void init() { ...
Spring Expression Language
C’est un langage d’expression très utilisé qui permet de variabiliser un paramètre qui serait fixe dans une chaîne, ce qui est plus particulièrement utile avec les paramètres des annotations.
Sa syntaxe est similaire à Jakarta Expression Langage (Unified EL) mais il offre des possibilités supplémentaires comme l’invocation de méthode sur un bean ou sur un objet hors Spring.
Il est possible d’utiliser SpEL de façon autonome via l’utilisation de quelques classes d’infrastructures d’amorçage telles que l’analyseur, mais en général l’utilisation est transparente quand nous codons avec Spring. Le SpEL est très proche des spécifications JavaBeans.
1. Utilisation de l’ExpressionParser
L’ExpressionParser permet d’évaluer une expression.
Nous l’utilisons rarement directement, mais il est utilisé par Spring très souvent avec des chaînes que nous lui envoyons.
ExpressionParser parser = new SpelExpressionParser ();
Expression exp = parser.parseExpression ( "'Bonjour à'.concat
(' vous !')");
String message = (String) exp.getValue ();
Par exemple ici, nous concaténons les chaînes « Bonjour à » et « vous ! ».
Il est possible d’accéder aux membres et méthodes des objets via une chaîne d’appels en utilisant le « . ».
Par exemple :
"'Bonjour à vous'.bytes.length"
invoque ’getBytes (). length’.
La méthode getValue a pour signature :
public <T> T getValue(Class<T> desiredResultType).
Cela permet de ne pas spécifier le type.
Nous utilisons en général l’ExpressionParser via un objet pivot ou racine :
Voiture voiture = new Voiture ( "Ford" , "blanche" );
ExpressionParser parser = new SpelExpressionParser ();
Expression exp = parser.parseExpression ( "marque" );
EvaluationContext context = new StandardEvaluationContext (voiture);
String marque = (String) exp.getValue (context);
Le getValue se fait alors sur un EvaluationContext spécifique.
2. L’EvaluationContext
Par défaut...
Points clés
Points à retenir :
-
Le framework est configurable en Java ou via des fichiers XML.
-
Nous utiliserons de préférence la configuration Java pour les nouveaux projets.
-
Les beans sont injectés grâce aux constructeurs ou par les setters.
-
Nous pouvons améliorer le système de log par défaut.
-
Il est très facile de faire des tests avec Spring grâce aux interfaces.