Injections de dépendances en Spring

Maven

Ces instructions sont à refaire chez vous sur votre ordinateur pour pouvoir travailler chez vous.

Pour utiliser Spring, il nous faut récupérer les jars de Spring dont nous aurons besoin. Plutôt que de le faire à la main, nous allons utiliser Maven.

Vérifiez que Maven est bien installé avec la commande:

$ mvn -v
Si ce n'est pas le cas installez-le depuis ce site.

Nous n'allons pas utiliser Maven directement mais plutôt l'IDE IntelliJ qui fournit une très bonne intégration avec Maven

IntelliJ a créer la structure d'un projet Maven. Votre code ira dans src/main/java. Il y a aussi un fichier XML pom.xml qui contient les paramètres de configuration de Maven.

Nous allons maintenant indiquer à Maven que nous voulons utiliser une partie du framework Spring. Pour cela, il faut rajouter dans le fichier pom.xml les dépendances suivantes. Il faut rajouter au-dessus de la balise </project>

Si le fichier pom.xml proposé par IntelliJ contient déjà une balise properties, il faut la supprimer pour la remplacer par la balise fournie ci-dessous.

<properties>
    <org.springframework.version>6.0.13</org.springframework.version>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.target>17</maven.compiler.target>
    <maven.compiler.source>17</maven.compiler.source>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${org.springframework.version}</version>
    </dependency>
</dependencies>

Si vous utilisez une autre version du SDK, il faut adapter les champs maven.compiler.

Pour qu'IntelliJ télécharge les librairies correspondant aux balises dependency, il faut cliquer sur l'icone .

Si vous allez dans File > Project Structure > Librairies, vous devriez voir que IntelliJ a téléchargé les librairies de Spring.

Mon premier bean

Toutes vos classes doivent être dans le package fr.uge.jee.printers.

Le main de la classe Application ci-dessous crée un SimpleMessagePrinter et appelle sa méthode printMessage.

public class SimpleMessagePrinter {

    public void printMessage(){
        System.out.println("Hello World!");
    }

}

public class Application {
    public static void main(String[] args) {
        var printer = new SimpleMessagePrinter();
        printer.printMessage();
    }
}

On veut que SimpleMessagePrinter soit géré par le conteneur IoC de Spring.

Créer un fichier config-printers.xml où vous allez déclarer le bean correspondant à SimpleMessagePrinter. Vous pouvez partir du fichier ci-dessous:

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

    <bean id="..." class="..."/>

</beans>

Le fichier config-printers.xml doit se trouver dans le répertoire src/main/resources/.

Une fois le bean déclaré, il faut créer un conteneur IoC de Spring à partir du fichier config-printers.xml avec le code suivant:

ApplicationContext context = new ClassPathXmlApplicationContext("config-printers.xml");

Modifiez la classe Application comme demandé.

Plusieurs afficheurs de messages

Cet exercice continu l'exercice précédent. Toutes vos classes doivent être dans le package fr.uge.jee.printers.

Nous voulons définir plusieurs afficheurs de message.

Créez une interface MessagePrinter et faites en sorte que SimpleMessagePrinter implémente de cette interface.

Créez une classe FrenchMessagePrinter qui implémente cette interface et qui affiche le message en français.

Faites changer votre afficheur de message pour un FrenchMessagePrinter en utilisant seulement le fichier config-printers.xml

Changez votre fichier de configuration config-printers.xml pour avoir un bean pour chacun des MessagePrinter. Modifiez ensuite votre classe Application pour instantier chacun de ces beans et appeler leur méthode printMessage.

La classe CustomizableMessagePrinter a un constructeur qui permet configurer le message afficher.

package fr.uge.jee.printers;

public class CustomizableMessagePrinter implements MessagePrinter {

    private String message;

    public CustomizableMessagePrinter(){
        this.message="Custom Hello World";
    }

    public CustomizableMessagePrinter(String message){
        this.message=message;
    }

    @Override
    public void printMessage() {
        System.out.println(message);
    }
}

Changez votre config-printers.xml pour rajouter un bean pour CustomizableMessagePrinter. Dans votre classe Application, instanciez le bean correspondant à la classe CustomizableMessagePrinter. Quel constructeur est appelé ?

Il est possible de passer un argument pour le constructeur au moment de la création du bean. Pour cela, il faut rajouter dans config-printers.xml la propriété suivante dans le bean concerné.

<bean id="..." class="...">
  <constructor-arg value="Argument for the constructor"/>
</bean>

Changez votre config-printers.xml pour qu'il définisse un bean pour la classe CustomizableMessagePrinter créé avec le message "Hello from the config.xml".

Singleton et lazyness

Cet exercice continue les deux exercices précédents. Toutes vos classes doivent être dans le package fr.uge.jee.printers.

On continue l'exercice précédent. On va dans un premier temps rajouter une classe CountMessagePrinter qui quand on appelle printMessage affiche un message du type "This message number 1" et incrémente ce nombre au prochain appel.

Dans votre fichier de configuration, rajoutez un bean pour CountMessagePrinter.

        MessagePrinter printer = context.getBean("printerServiceC   ount",MessagePrinter.class);
        printer.printMessage();
        printer.printMessage();
        printer.printMessage();
        MessagePrinter printer2 =  context.getBean("printerServiceCount",MessagePrinter.class);
        printer2.printMessage();

Expliquez l'affichage ci-dessous ? Quel design pattern est mis en oeuvre par le conteneur IoC ?

This is message number 0
This is message number 1
This is message number 2
This is message number 3

On peut changer la façon dont le conteneur IoC crée les beans en changeant le scope du bean. Pour plus d'information sur les scopes d'un bean, vous pouvez vous référer à la documentation de Spring.

Changez votre fichier de configuration pour qu'un CountMessagePrinter différent soit crée à chaque appel à getBean.

Le but de la fin de l'exercice est comprendre quand les beans sont instantiés. Est-ce que c'est au moment de la création de ApplicationContext ou au moment de l'appel au premier getBean pour ce bean ?

Ecrivez une classe SlowConstructionMessagePrinter qui simule un bean lent à instantié en attendant 5 secondes dans le constructeur. Definissez un bean correspondant dans le fichier de configuration.

A quel moment sont instantiés les beans ?

En regardant la documentation de Spring, faites en sorte que le bean correspondant à SlowConstructionMessagePrinter ne soit instantié à la création de ApplicationContext.

Quel est l'inconvéniant majeur de déférer l'instantition des beans ?

Autowire et Collections

Cet exercice continue les deux exercices précédents. Toutes vos classes doivent être dans le package fr.uge.jee.bookstore. Votre fichier de configuration se nommera config-bookstore.xml.

Codez l'exemple du cours sur Library et Book. Dans votre fichier de configuration XML, créez 3 beans pour 3 livres et une Library qui contient deux de ces livres. Créez un classe Application pour créez le bean de la Library et l'afficher avec sa méthode toString.

Utilisez autowire="constructor" pour définir le bean associé à Library en pensant bien à supprimer les arguments! Qu'observez vous ?

Online Shopping

Toutes vos classes doivent être dans le package fr.uge.jee.onlineshop et votre fichier XML doit s'appeler config-online.xml.

Dans cet exercice, on veut faire un sorte de modéliser un magasin en ligne. Le but est simplement de faire construire par le conteneur IoC de Spring un bean pour OnlineShop que l'on puisse entièrement configurer. La classe OnlineShop ne fait rien à part afficher sa configuration.

Le magasin peut avoir jusqu'à deux modes de livraison: le mode classique et le mode par drone. Le mode classique a un délai de livraison moyen qui sera injecté depuis le fichier de configuration. De plus, le magasin offre jusqu'à deux assurances : l'assurance pour les retours (qui peut être soit uniquement réservée aux membres soit ouverte à tous) et l'assurance contre le vol.

public class OnlineShop {

    private final  String name;
    private final Set<Delivery> deliveryOptions;
    private final Set<Insurance> insurances;

    ....

    public void printDescription(){
        System.out.println(name);
        System.out.println("Delivery options");
        deliveryOptions.forEach(opt -> System.out.println("\t"+opt.description()));
        System.out.println("Insurance options");
        insurances.forEach(insurance -> System.out.println("\t"+insurance.description()));
    }
}

Ecrivez les classes et les interfaces nécessaires et complétez la classe OnlineShope.

La situation est simplifiée à l'extrême. On vous demande juste de faire une interface pour les assurances avec simplement une méthode String description() et une interface pour les méthodes de livraison avec aussi une méthode String description()

Ecrivez un fichier XML et une classe Application dont le main utilise le conteneur IoC de Spring pour créer un magasin et affiche sa description. On veut le résultat ci-dessous:

Amazon
Delivery options
  Delivery by drone
  Standard Delivery with a delay of 5 days
Insurance options
  Return insurance only for members  

En modifiant uniquement votre fichier XML faites en sorte que la configuration du magasin change pour obtenir l'affichage ci-dessous. Copiez votre fichier de configuration dans config-onlineshopv2.xml.

AhMaZone
Delivery options
  Standard Delivery with a delay of 999 days
Insurance options
  Return insurance
  Thief insurance

Modifier votre fichier de configuration pour ne pas avoir besoin de vous baser sur le nom des beans. Copiez votre fichier de configuration dans config-onlineshopv3.xml.

Il est possible d'omettre le champ id dans la définition d'un bean. Votre fichier config-onlineshopv3.xml ne doit contenir aucune id.