Blog ENI : Toute la veille numérique !
🐠 -25€ dès 75€ 
+ 7 jours d'accès à la Bibliothèque Numérique ENI. Cliquez ici
Accès illimité 24h/24 à tous nos livres & vidéos ! 
Découvrez la Bibliothèque Numérique ENI. Cliquez ici
  1. Livres et vidéos
  2. Java Spring
  3. Le conteneur Spring
Extrait - Java Spring Le socle technique des applications Jakarta EE (4e édition)
Extraits du livre
Java Spring Le socle technique des applications Jakarta EE (4e édition) Revenir à la page d'achat du livre

Le conteneur Spring

Introduction

Ce chapitre présente une utilisation simplifiée de Spring afin que nous ayons un aperçu de Spring sans nous perdre dans les détails. Nous verrons par la suite des éclairages sur des parties qu’il vaut mieux connaître pour bien se repérer dans une application plus complexe. L’exemple qui l’illustre permet déjà d’expérimenter une grande partie des problématiques relatives à l’utilisation de ce framework.

Les origines

Nous allons aborder les principaux composants de Spring.

Spring est un framework qui simplifie la programmation. Il est composé d’un cœur, Spring Core, qui permet une gestion simple des instances de classe en mémoire et de bibliothèques de classes qui utilisent ce cœur.

Celles-ci s’appellent des beans Spring. Le framework fournit un ensemble de beans préprogrammés qui couvrent un très large spectre de cas d’utilisation que l’on rencontre quand nous codons une application complexe.

Ils sont extensibles et faciles à utiliser.

Le cœur du framework permet de charger au démarrage un ensemble de singletons et facilite ensuite leur accès en injectant automatiquement leur référence (emplacement mémoire) dans les objets qui les utilisent.

Spring permet d’avoir un contrôle très fin sur la gestion des objets en mémoire.

Spring s’interface avec énormément de frameworks et de produits. Le principal intérêt de Spring est l’instanciation et la mise à disposition automatisée de beans. Ces objets peuvent être de deux types : des singletons, comme nous l’avons déjà évoqué, mais aussi des objets « dupliqués » nommés « prototypes ».

Contrairement à l’objet...

Les modules fondamentaux

Les beans spécialisés présentés ci-après sont généralement présents depuis la version 0.9 de Spring et ont reçu des améliorations au fil des versions. Ils sont surtout utilisés quand nous souhaitons étendre Spring. Vous les verrez surtout dans les frameworks. Quand nous utilisons les annotations nous ne voyons plus ces objets, mais ils sont utilisés en interne dans Spring.

1. Composition d’un bean

Nous verrons plus en détail le fonctionnement d’un bean dans le chapitre Programmation orientée aspect avec Spring consacré à l’AOP.

Pour simplifier, un bean peut être considéré comme un proxy qui augmente un objet Java. Le proxy permet :

  • un détournement des appels aux méthodes de l’objet pour ajouter des comportements ;

  • l’ajout de nouvelles méthodes ;

  • une gestion des liens vers les objets référencés dans l’objet principal ;

  • une possibilité de valoriser les propriétés de l’objet via de multiples façons : chaînes, fichiers (via une factory) ;

  • une gestion de messages dans des bundles (via le contexte) ;

  • une gestion d’événements interobjets : création, destruction, événements utilisateurs.

Le bean est orienté données (POJO) ou traitements en essayant de séparer ces aspects dans des beans spécialisés.

Il contient donc :

  • la classe d’implémentation réelle du bean ;

  • des éléments de configuration comportementale du bean, singleton/prototype ;

  • les beans liés à l’injection des dépendances…

  • des valeurs de propriété à définir à la construction.

Quand nous voulons utiliser un bean Spring, nous ajoutons un membre de classe et nous indiquons à Spring que nous voulons l’utiliser en typant la variable avec l’interface correspondant au Bean. Spring a différente stratégie pour trouver le bon Bean à injecter.

Un bean est identifié par son nom et son identifiant. Les identifiants peuvent posséder des alias, mais cette utilisation est plutôt une source de confusion. Plusieurs beans peuvent porter le même identifiant, on indiquera alors...

Configuration des beans

Comme nous l’avons évoqué, la configuration des beans Spring peut être implicite ou explicite. En mode implicite, Spring reconnaît de lui-même les classes Java à ajouter à la liste des beans à partir d’un balisage sur les candidats. En mode explicite, nous indiquons à Spring le nom et l’emplacement exact des beans. Le mode explicite est plus sécurisé mais il est plus long à configurer. C’est pourquoi il est rarement utilisé. Les beans sont configurables par l’utilisation de fichiers XML, d’annotations ou directement en Java.

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).

Nous pouvons donc par ce biais déclarer un arbre d’objets contenant des objets singletons et des objets prototypes. Nous verrons qu’il est aussi possible d’avoir une configuration différente pour le « run » et pour les test en ayant par exemple une configuration dédiée.

La configuration par XML présente des difficultés liées à l’utilisation de configuration au format texte. Spring doit en effet faire des conversions du texte vers le type cible. Pour indiquer un nombre, nous le mettrons dans une chaîne, qui est ensuite convertie. Pour indiquer le nom d’une variable ou d’un paramètre, nous utilisons aussi du texte. Les problèmes ne se voient alors qu’à l’exécution alors qu’en configurant en Java, les problèmes se révèlent directement lors de la phase de compilation.

Nous utiliserons donc en priorité la configuration Java. Les exemples montrent tous les types de configuration afin de les présenter.

1. Configuration par un fichier XML

Les applications simples ou anciennes sont configurées directement dans un ou plusieurs fichiers XML organisés de façon hiérarchique. Nous avons en général un fichier XML principal par conteneur Spring, accompagné...

Utilisation des beans : injection par setters et constructeurs

Le bean devient disponible pour l’application une fois que le bean est déclaré dans la configuration. Nous pouvons alors l’injecter comme membre de notre classe.

Spring ne peut injecter des dépendances que dans un bean Spring.

Les connaisseurs vous diront qu’il est possible, en paramétrant la JVM, d’injecter les beans où on le souhaite, en modifiant le class loader, mais dans un contexte d’utilisation normal, nous nous limiterons aux beans Spring.

Le premier bean de notre application sera donc obligatoirement instancié par une factory Spring qui chargera le contexte et appliquera la configuration à ce bean. C’est le point de démarrage de Spring dans l’application.

Spring crée l’objet avec le constructeur par défaut et appelle ensuite les setters pour injecter les références des objets à injecter. Si le constructeur par défaut n’est pas disponible, Spring utilise alors un constructeur avec des arguments, ce qui permet aussi de renseigner les dépendances. Cette possibilité est utilisée pour les API externes à Spring qui ne disposent pas de constructeurs par défaut comme les pools de connexions pour lesquels nous devons passer la datasource en paramètre de constructeur.

Un constructeur qui contient en paramètre des beans permet de faire l’équivalent de l’annotation @Autowired sur des membres du bean. Il faut par contre avoir un nombre limité d’arguments dans le constructeur pour garder de la lisibilité. 

L’annotation @Autowired est mise sur la variable ou préférentiellement sur le setter de la variable. Il est possible de spécifier que la résolution peut être différée au démarrage via le paramètre required=false :

@Autowired(required=false) 

Ceci permet de préciser à Spring que le bean à mapper peut ne pas être disponible au démarrage. Par exemple, si un bean a besoin lors de son instanciation (chargement en mémoire), d’un élément qui n’est pas disponible au démarrage, il est possible de différer son instanciation au moment de sa première utilisation. Un bean peut par exemple avoir besoin...

Contrôle du cycle de vie : construction et destruction

Spring permet d’intercepter la création et la description des objets Spring.

Il n’est pas possible d’utiliser les beans injectés directement dans le constructeur car à ce moment précis, Spring n’a pas encore instancié les objets contenus dans les membres de la classe. De même, l’appel à la méthode finally n’est pas simple car elle est appelée par le garbage collector et le finally est deprecated (JEP 421). Spring donne la possibilité de marquer des méthodes pour qu’elles soient appelées à la création et à la destruction de l’objet. Il existe plusieurs mécanismes.

Voici le plus récent.

Annotation

Moment de l’appel

@PostConstruct

Après le constructeur, avec les beans initialisés.

@PreDestroy

Avant la destruction.

Il est aussi possible d’utiliser les aspects de la programmation orientée aspect (AOP) pour effectuer des actions lors de la création et la destruction de l’objet.

Nous utilisions avant ces annotations des méthodes paramétrées dans la configuration avec les paramètres init-method et destroy-method comme dans l’exemple suivant :

<bean id="helloWorld" 
   class="com.tutorialspoint.HelloWorld"  
   init-method="init"...

Exemple illustrant les mappings standards

Cet exemple décrit, pas à pas, la création d’un projet simple. Il est présent dans les exemples téléchargeables depuis la page Informations générales. Ce chapitre peut paraître un peu complexe aux débutants de prime abord, mais pourra être réutilisé pour expérimenter.

1. Le projet Maven

  • Générez un projet Maven vide avec l’archétype suivant :

mvn archetype:generate -DgroupId=fr.eni.editions  
-DartifactId=mappingApp -DarchetypeArtifactId=maven-archetype-quickstart 
-DinteractiveMode=false 
  • Ou utilisez le Spring Initalizr pour créer un projet vide (https://start.spring.io/).

  • Importez le fichier Maven dans Eclipse ou IntelliJ IDEA (ou dans votre outil de développement préféré).

  • Afin de séparer les fichiers de configuration des fichiers de code, créez les répertoires de sources :

  • src/main/resources

  • src/test/resources

Nous mettrons dans ces répertoires les fichiers de configuration de Spring et du système de log.

2. Fichier de configuration de Spring

Nous allons créer un fichier Spring nommé applicationContext.xml et le placer dans le répertoire src/main/resources/Spring/.

<?xml version="1.0" encoding="UTF-8"?> 
<beans default-lazy-init="true"  
xmlns="http://www.springframework.org/schema/beans"  
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
xmlns:context="http://www.springframework.org/schema/context"  
xmlns:jdbc="http://www.springframework.org/schema/jdbc" 
   xsi:schemaLocation=" 
     http://www.springframework.org/schema/beans  
http://www.springframework.org/schema/beans/spring-beans-4.1.xsd 
     http://www.springframework.org/schema/jdbc  
http://www.springframework.org/schema/jdbc/spring-jdbc-4.1.xsd 
     http://www.springframework.org/schema/context...

Le 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...

Serveurs J2EE, Java EE et Jakarta EE

1. Généralités

Spring a été initialement conçu pour éviter l’utilisation des EJB qui manquaient, à l’origine, de maturité. Dans ses premières versions, Spring permettait une gestion simplifiée des EJB. Encore aujourd’hui, des projets continuent à utiliser Spring 3 avec des serveurs Java JEE 6, voire Spring 2 associé à un serveur Java JEE 5, avec en général un framework maison. On évite parfois une migration si les modifications du code sont mineures. On retrouve dans Spring 2 et 3 presque tout ce qu’il y a dans Spring 4, en dehors des annotations. En effet, Spring a essayé dans la mesure du possible de reporter les évolutions de la version en cours dans les versions antérieures. Spring, dans toutes ses versions, fonctionne très bien sur la majorité des serveurs J2EE, Java EE et Jakarta EE (sous réserve d’utiliser la version qui correspond au serveur employé).

Dans la pratique, les "problèmes" arrivent cependant assez vite. Pour commencer, il y a les implémentations "propriétaires" des JVM. Une JVM IBM ne possède pas les mêmes bugs, les mêmes failles, qu’une JVM Oracle. Cela signifie que le code n’est pas réellement portable. Il faut tenir compte également du rythme des versions de corrections et des difficultés de mise à disposition des patchs dans les différents environnements.

Il y a ensuite les parties personnalisées : WebSphere, WebLogic et jBoss. Ceux-ci proposent une partie commune, d’un point de vue des spécifications, mais chacun possédera ses subtilités pour les implémenter et il faudra donc les connaître. En outre, ils proposent aussi des parties différenciantes qui vont faciliter énormément la vie des développeurs mais les lier définitivement avec un fournisseur de serveurs. Leur but est de vendre un package avec des machines, des licences... Ces ponts permettent en général d’appeler du code "Legacy" depuis du code Java.

IBM propose un pont avec ses mainframes permettant d’utiliser IMS, CISC... conjointement avec Java. WebLogic propose un pont avec Tuxedo et des workflows...

Points clés

  • 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.

  • Il est possible d’utiliser conjointement Spring et les EJB.