Tutoriel AspectJ : création aspect LOG aspectJ

| Objis | Baptiste Wicht |

Favorisez la modularité, la réutilisabilité et la maintenance de vos applications avec AspectJ. Libérez-vous du codage 'en dur' de traces dans vos classes. Éliminez tout code 'System.out.println' ou 'log4j' dans vos classes applicatives. Mettez en œuvre AspectJ en créant un premier 'aspect' LOG. Utilisez le compilateur ajc pour compiler aussi bien des fichiers Java (.java) que des fichiers AspectJ (.aj) et réaliser un tissage. Interceptez la construction d'un objet, le changement d'état d'une variable ou le lancement d'une exception. Analysez des exemples de point de jonction (JoinPoint), coupes (pointcut), greffon (advice) AspectJ

Commentez ♪

- Titre : Tutoriel AspectJ : création aspect LOG aspectJ

- Auteurs : Objis, Baptiste Wicht

- Parution : 17 janvier 2010

- Licence : Copyright ® 2009 Objis. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Introduction

- II. Prérequis

- III. Objectifs

- IV. Programme

- V. Contexte

- VI. Partie 1 : tracer sans aspectJ.

- VII. Partie 2 : tracer avec aspectJ

- VIII. Partie 3 : deuxième version de l'aspect

- IX. Partie 4 : Aspect Profiling

- X. Conclusion

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 :

1. Vous déclarez un aspect à travers le mot clé 'aspect'. Ici l'aspect LogAspect ;

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

3. 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() ;

4. 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()) ;

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

Commentez