I. Introduction▲
Ce tutoriel est un extrait de séances pratiques de la formation SPRING dispensée par Objis.
II. Prérequis▲
- Installation JDK.
- Installation kit de développement AspectJ (AJDK).
III. Objectifs▲
- Créer et mettre en œuvre un aspect de LOG.
- Comprendre le lien entre le compilateur aspectJ (ajc) et le compilateur Java (javac).
- Déclarer un aspect, une coupe, un point de jonction avec AspectJ.
- Comprendre la valeur ajoutée de la programmation Aspects.
IV. Programme▲
- Contexte :application bancaire.
- Partie 1 : tracer les retraits d'argent sans aspectJ.
- Partie 2 : tracer les retraits d'argent avec un aspect AspectJ de log : LogAspect.aj.
- Partie 3 : nouvelle version de l'aspect de LOG.
- Partie 4 : mise en œuvre d'un profiling.
Durée totale : 40 min.
V. Contexte▲
Dans le cadre d'un projet d'envergure pour un établissement financier de la place, vous devez tracer les opérations de retrait d'argent de tout compte bancaire. Vous devez être capable de tracer l'état du compte AVANT le retrait et l'état du compte APRÈS le retrait.
VI. Partie 1 : tracer sans aspectJ.▲
Fichiers à créer
Créez un fichier CompteBancaire.java qui sera la classe métier représentant un compte bancaire. Ajoutez une propriété solde ainsi que getter/setter puis constructeur. Ajoutez enfin les méthodes de retrait et dépôt.
package
com.objis.demoaspectj.banque;
public
class
CompteBancaire {
private
int
solde;
public
CompteBancaire
(
int
solde) {
super
(
);
this
.solde =
solde;
}
public
void
depot (
int
sommeDepot){
solde =
solde +
sommeDepot;
}
public
void
retrait (
int
sommeRetrait){
solde =
solde -
sommeRetrait;
}
public
int
getSolde
(
) {
return
solde;
}
public
void
setSolde
(
int
solde) {
this
.solde =
solde;
}
}
Créez un fichier Main.java qui sera la classe principale de l'application (il contiendra la méthode main()). Elle va instancier un compte bancaire et réaliser une opération de retrait.
package
com.objis.demoaspectj;
import
com.objis.demoaspectj.banque.CompteBancaire;
public
class
Main {
public
static
void
main
(
String[] args) {
// 1 : Création d'un compte avec solde initial
CompteBancaire monCompte =
new
CompteBancaire
(
1000
);
// 2 : Retrait
System.out.println
(
"AVANT le retrait"
);
// 3 : Retrait
monCompte.retrait
(
300
);
// 4 : Retrait
System.out.println
(
"APRÈS le retrait"
);
}
}
REMARQUE : sur les quatre étapes ci-dessus :
- deux étapes ne concernent pas directement le 'métier'. En effet les étapes 2 et 4 sont liées à une préoccupation 'technique' : celle de tracer un évènement (ici le retrait) ;
- deux étapes concernent le métier. En effet les étapes 1 et 3 sont liées à des préoccupations directement liées au métier bancaire : créer un compte et effectuer un retrait.
INFO : le 'tisseur d'aspects' AspectJ va nous permettre par la suite d'isoler cette préoccupation technique dans un fichier distinct : le fichier LogAspectj.aj qui représente l'aspect LOG.
Compilez les classes avec le compilateur javac (issu du kit de développement Java).
javac CompteBancaire.java Main.java
Vous obtenez ceci.
Rangez les classes générées (Main.class et CompteBancaire.class) respectivement dans les répertoires com/objis/demoaspectj/ et com/objis/demoaspectj/banque.
Lancez l'exécution de la classe principale : Main
java - cp .;lib\aspectjrt.jar com.objis.demoaspectj.Main
Avant le retrait
Après le retrait
Nous obtenons bien les traces qui précèdent et suivent l'appel à la méthode retrait().
Pour cela nous avons dû écrire 'en dur' dans la classe cliente les lignes suivantes :
- System.out.println(« AVANT le retrait ») ;
- System.out.println(« APRÈS le retrait »).
Nous aurions également pu écrire ces lignes dans la méthode retrait() de la classe appelée (CompteBancaire).
Et s'il était possible d'afficher les mêmes traces sans écrire ces lignes ni dans la classe appelante (ici Main), ni dans la classe appelée ?
C'est là qu'intervient un tisseur d'aspect comme AspectJ.
VII. Partie 2 : tracer avec aspectJ▲
Dans cette partie vous allez mettre le tisseur d'aspect AspectJ en action. Vous allez supprimer tout code de Log dans vos classes et centraliser la gestion des logs dans un aspect aspectJ : LogAspect.aj
Vous allez utiliser le compilateur ajc (surcouche du compilateur javac) pour compiler aussi bien l'aspect LogAspect.aj que les classes Main.java et CompteBancaire.java.
Modifiez le contenu de la classe Main de la façon suivante :
package
com.objis.demoaspectj;
import
com.objis.demoaspectj.banque.CompteBancaire;
public
class
Main {
public
static
void
main
(
String[] args) {
// 1 : Création d'un compte avec solde initial
CompteBancaire monCompte =
new
CompteBancaire
(
1000
);
// 2 : Retrait
monCompte.retrait
(
300
);
}
}
Comme vous le constatez, il n'y a aucune ligne associée au Log . C'est un aspect LogAspect.aj que nous allons créer qui va 'intercepter' toute demande de retrait.
Créez un fichier LogAspect.aj et ajoutez le contenu suivant :
package com.objis.demoaspectj.aspects;
public aspect LogAspect {
pointcut logRetrait()
: execution(* com.objis.demoaspectj.banque.CompteBancaire.retrait(..));
before() : logRetrait() {
System.out.println("Avant le retrait");
}
after() : logRetrait() {
System.out.println("Après le retrait");
}
}
Explications :
- Vous déclarez un aspect à travers le mot clé 'aspect'. Ici l'aspect LogAspect ;
- Vous déclarez une coupe nommée logRetrait() à travers le mot clé 'pointcut'. Une coupe est un ensemble de points de jonction (Moments d'exécution où il se passe quelque chose qui vous intéresse. C'est l'équivalent de point d'arrêt lors d'un débogage) ;
- Vous déclarez l'ensemble des points de jonction. Ici toute méthode retrait() de la classe com.objis.demoaspectj.banque.CompteBancaire, quelque soit le nombre de paramètres de la méthode retrait (..) et quel que soit le type de retour (*) de la méthode retrait() ;
- Vous déclarez un greffon type before() : le code System.out.println(« AVANT le retrait ») ; sera lancé juste avant tout point de jonction (c'est-à-dire ici toute exécution de la méthode retrait()) ;
- Vous déclarez un greffon type after() : le code System.out.println(« APRÈS le retrait ») ; sera lancé juste après tout point de jonction (c'est-à-dire ici toute exécution de la méthode retrait()).
ça y est : vous avez codé votre premier aspect 100 % aspectJ. Reste à la compiler.
Compiler l'ensemble des classes Main.java, CompteBancaire.java et LogAspect.aj en utilisant le compilateur ajc (aspectj compiler) installé avec le kit de développement AspectJ (AJDK) :
ajc Main.java CompteBancaire.java LogAspect.aj
Vous obtenez ceci :
Rangez les classes générées (Main.class, CompteBancaire.class et LogAspectj.class) respectivement dans les répertoires com/objis/demoaspectj/ , com/objis/demoaspectj/banque et com/objis/demoaspectj/aspects.
Remarque : la commande suivante crée pour vous l'arborescence :
ajc -d . Main.java CompteBancaire.java LogAspect.aj
Créez un répertoire 'lib' et ajoutez le jar aspectjrt présent dans ASPECTJ_HOME\lib . L'aspect aura besoin de ce jar à l'exécution.
Lancez l'exécution de la classe Main :
java - cp .;lib\aspectjrt.jar com.objis.demoaspectj.Main
Avant le retrait
Après le retrait
Le résultat est le même que dans la partie 1. Mais le code de notre classe principale est plus léger. Nous nous sommes concentrés sur le métier et non sur une préoccupation de log.
VIII. Partie 3 : deuxième version de l'aspect▲
Expliquez l'effet de l'aspect suivant :
package com.objis.demoaspectj.aspects;
import com.objis.demoaspectj.banque.CompteBancaire;
public aspect LogAspect2 {
pointcut logRetrait(CompteBancaire compte, int sommeRetrait) : call(void com.objis.demoaspectj.banque.CompteBancaire.retrait(int))
&& target(compte)
&& args(sommeRetrait);
before(CompteBancaire compte, int sommeRetrait) : logRetrait(compte, sommeRetrait) {
System.out.println("Avant le retrait de " + sommeRetrait + " euros du compte " + compte);
}
after(CompteBancaire compte, int sommeRetrait) : logRetrait(compte, sommeRetrait) {
System.out.println("Après le retrait de " + sommeRetrait + " euros");
}
}
Analysez le résultat :
java - cp .;lib\aspectjrt.jar com.objis.demoaspectj.Main
Avant le retrait de 300 euros du compte com.objis.demoaspectj.banque.CompteBancaire@9304b1
Après le retrait de 300 euros
Vous découvrez ici une technique permettant de passer à un greffon le contexte d'exécution du point de jonction.
Expliquez l'effet du code suivant :
// Intercepter le constructeur de la classe CompteBancaire
pointcut constructeur() : call (CompteBancaire.new(..));
before () : constructeur(){
System.out.println("Avant methode constructeur");
}
after () : constructeur(){
System.out.println("Après methode constructeur");
}
}
Ajoutez ce code à l'aspect.
Exécutez.
IX. Partie 4 : Aspect Profiling▲
Analysez le code suivant
package com.objis.demoaspectj.aspects;
public aspect ProfilingAspect {
pointcut publicOperation() : execution(public * *.*(..));
Object around() : publicOperation() {
long debut = System.nanoTime();
Object ret = proceed();
long fin = System.nanoTime();
System.out.println(thisJoinPointStaticPart.getSignature() + " a pris " + (fin-debut) + " nanoseconds");
return ret;
}
}
- À quoi sert cet aspect ?
- Où est la coupe ?
- Combien y a-t-il de greffons ? De quel type ?
Comprendre thisJoinPointStaticPart
Remarque : la variable thisJoinPointStaticPart est une des 3 variables disponibles dans chaque greffon.
Cette variable apporte des informations à propos du point de jonction courant.
Exemples d'info :
- signature de la méthode ;
- l'objet this ;
- les arguments de la méthode.
Mise en œuvre
À VOUS DE JOUER : utilisez le compilateur ajc pour compiler cet aspect avec le programme principal.
Résultat attendu :
java - cp .;lib\aspectjrt.jar com.objis.demoaspectj.Main
Avant le retrait
Après le retrait
void com.objis.demoaspectj.banque.CompteBancaire.retrait(int) a pris 2250064 nanoseconds
void com.objis.demoaspectj.Main.main(String[]) a pris 90539444 nanoseconds
Expliquez.
X. Conclusion▲
Dans ce tutoriel, vous avez mis en œuvre AspectJ à travers la création d'un aspect LogAspect.aj.