Injections de dépendances en Spring

Configuration utilisant les annotations

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

Créez un nouveau projet Maven avec le même pom.xml que dans le TD précédent.

Reprenez les classes du dernier exercice du TD précédent. Asssemblez le magasin Amazon décrit dans l'exercice précédent en utilisant uniquement une classe de configuration (pas de @Autowireet pas de @Component).

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

Réalisez le même assemblage en utilisant la classe de configuration reduite à son strict minimum. Vous utiliserez @Component, @Value.

Mettez votre classe de configuration de la question précédente en commentaire.

@Configuration
@ComponentScan
public class Config {
}

Modifiez votre injection pour que les valeurs: nom, delay, ... soient lues dans une fichier nommé onlineshop.properties qui se trouvera dans le répertoire resources.

onlineshop.name=AmaZone
onlineshop.standarddelivery.delay=999
onlineshop.returninsurance.membersonly=false

Lisez la documentation de @Value.

Où cacher son token ?

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

Le code ci-dessous montre le code simplifié d'une application qui utilise un token pour accéder à une API pour envoyer des messages.

@Configuration
@ComponentScan
public class Application {

    public static void main(String[] args) {
        var applicationContext = new AnnotationConfigApplicationContext(Application.class);
        var messenger = applicationContext.getBean(Messenger.class);
        messenger.send("Hello !");
    }
    
}

@Component
public class Messenger {

    @Value("A123G32G234H34245234")
    private String token;

    public void send(String message){
        System.out.println("Using the super secret token "+token+" to send the message : "+message);
    }
}

Le code ci-dessus à deux problèmes : il fait un injection directement sur le champ au lieu de faire une injection par le constructeur et letoken est accessible dans le git qui héberge le code source. Une bonne pratique est de ne stocker le token que sur le serveur de production. Cela peut se faire par exemple en passant le token par une variable d'environement.

Modifiez le code pour récuréper le token dans une variable d'environment nommée MESSENGER_TOKEN et pour faire une injection par le constructeur.

Pour tester votre code, il faut pouvoir lancer votre application depuis la ligne de commande. Vous pouvez voir comment faire ici.

Une fois Maven configuré, vous pouvez tester dans un terminal avec:

$ export MESSENGER_TOKEN=....
$ mvn exec:java

AOP in action

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

Dans cet exercice, on part d'une application qui n'est pas gérée par Spring et qui gère l'inscription des élèves à des cours. Pour des raisons de simplicité, elle simule les accès à la base de donnée. Cette application se trouve dans l'archive studentversion.tgz.

Notre but est d'utiliser l'AOP pour logger et auditer les performances de cette application.

Il faut rajouter les dépenses suivantes dans votre fichier pom.xml et rajouter:

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-aop</artifactId>
  <version>6.0.13</version>
</dependency>
<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjweaver</artifactId>
  <version>1.9.20.1</version>
</dependency>

Transformez le code qui vous ait donné pour faire en sorte que RegistrationService soit un bean géré par Spring.

Maintenant injectez un code qui affiche:

Vous allez devoir injecter ce code dans RegistrationService, il faut donc que RegistrationService soit géré le conteneur IoC de Spring.

Modifiez votre injection de code pour obtenir l'affichage suivant:

Calling createStudent with arguments [Harry,Potter]
Return id 0 by createStudent
Calling createStudent with arguments [Hermione,Granger]
Return id 1 by createStudent
Calling createStudent with arguments [Ron,Wesley]
Return id 2 by createStudent
Calling createLecture with arguments [Potions]
Return id 0 by createLecture
Calling createStudent with arguments [Draco,Malfoy]
Return id 3 by createStudent
Calling createLecture with arguments [Detention]
Return id 1 by createLecture

On veut maintenant chronométrer par AOP toutes méthodes saveToDB et loadFromDB. On veut un affichage du type suivant à la fin de l'exécution de Application:

DB timing report:
  saveToDB
    Mesured access times : [204, 201, 203]
    Average time :202.66666666666666
  loadFromDB
    Mesured access times : [114, 101]
    Average time :107.5  

Caching on demand (Bonus)

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

Spring offre la possibilité de réaliser le tissage sur les méthodes marquées par une annotation que nous aurons défini.

package fr.uge.jee;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SayHello {

}

Ce code définit une annotation SayHello. L'annotation @Target indique que l'annotation que s'applique uniquement à des méthodes.

On peut ensuite faire le tissage uniquement sur les méthodes qui sont annotées par l'annotation @SayHello avec un aspect qui ressemblera à:

@Aspect
@Component
public class TraceTimeAspect {

    @Around("@annotation(fr.uge.jee.SayHello)")
    public Object traceTimeAdvice(ProceedingJoinPoint point) throws Throwable {
      ...
    }
}    

Dans cet exercice, on vous demande de créer un annotation @Timed et un aspect qui va chronométrer tous les appels à des méthodes annotées et qui sera capable de nous afficher pour chaque méthode appelée au moins une fois la moyenne du temps pris par la méthode.

Votre aspect doit être thread-safe car les méthodes que vous allez chronométrées ne sont pas nécessairement exécutées dans un seul thread.