IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Développons en Java v 2.20   Copyright (C) 1999-2021 Jean-Michel DOUDOUX.   
[ Précédent ] [ Sommaire ] [ Suivant ] [ Télécharger ]      [ Accueil ] [ Commentez ]


 

85. La mise en oeuvre de l'AOP avec Spring

 

chapitre 8 5

 

Niveau : niveau 5 Confirmé 

 

L'AOP permet de facilement mettre en place des fonctionnalités dans différents points d'une application. Ces fonctionnalités sont désignées sous le terme advice : elles sont exécutées lors d'événements nommés joinpoint (par exemple l'invocation d'une méthode ou d'un constructeur, ...).

Les endroits où les advices seront invoqués lorsque le joinpoint est réalisé sont définis grâce à des pointcuts.

Une opération de tissage est nécessaire pour permettre l'exécution des aspects au runtime : ce tissage peut être réalisé dynamiquement (grâce à un classloader ou la création de proxys) ou par compilation.

L'AOP est particulièrement intéressante pour mettre en oeuvre certaines fonctionnalités techniques transverses comme les transactions. C'est d'ailleurs grâce à l'AOP que les transactions sont gérées par Spring. La gestion des transactions devient alors déclarative et ne requiert plus de code supplémentaire utilisant une API dédiée.

L'AOP est un des mécanismes importants utilisés par Spring : il l'utilise lui-même pour mettre en oeuvre certaines fonctionnalités notamment les transactions, l'annotation @Configurable, ROO, ...

Ainsi, l'AOP peut être utilisée :

  • Indirectement, lors de l'utilisation de ces fonctionnalités
  • Directement, pour mettre en oeuvre ses propres Aspects : Spring facilite alors cette mise en oeuvre

Spring met en oeuvre l'AOP de deux façons :

  • Spring AOP : solution de Spring reposant sur des proxys créés dynamiquement
  • AspectJ : solution open source du projet Eclipse qui permet un tissage des aspects au runtime ou à la compilation par enrichissement du bytecode. Depuis la version 2.0, Spring propose un support AspectJ pour la mise en oeuvre de l'AOP en complément de sa propre solution reposant sur les proxys.

L'AOP peut être mise en oeuvre via Spring AOP ou AspectJ de plusieurs manières :

  • Avec AspectJ avec un tissage au chargement (Load Time Weaving) ou à la compilation
  • Avec ou sans les annotations AspectJ avec Spring AOP
  • Avec un mixte de Spring AOP et AspectJ

La mise en oeuvre peut donc se faire par déclaration dans le fichier de configuration ou par des annotations selon la solution de tissage utilisée. Toutes les combinaisons de syntaxe de déclaration avec la méthode de tissage ne sont pas possibles :

Syntaxe AspectJ

Annotation style AspectJ

XML dans la définition du context

Tissage par Spring

Non

Oui

Oui

Tissage par AspectJ

Oui

Oui

Non


Spring AOP ne permet qu'un tissage au runtime qui va créer des proxys dynamiquement lors du chargement du contexte, selon la configuration indiquée.

Spring AOP ne propose pas un support des fonctionnalités de programmation orientée aspect aussi poussé que celui proposé par AspectJ. La mise en oeuvre de Spring AOP ne peut se faire que sous certaines conditions :

  • Seuls les points de jonction liés à l'exécution d'une méthode sont supportés
  • Les aspects Spring AOP sont définis dans la configuration du contexte : ils ne peuvent donc s'appliquer que sur des objets gérés par le conteneur Spring car ils reposent sur des proxys exécutés dynamiquement.
  • Les aspects ne peuvent être appliqués que sur des méthodes public et non static

Ce chapitre contient plusieurs sections :

 

85.1. Spring AOP

Spring AOP est un module du framework Spring qui permet une mise en oeuvre d'une partie des fonctionnalités de l'AOP. Il propose un tisseur d'aspects sous la forme de proxys qui sont créés dynamiquement au runtime.

Depuis la version 2.0, la définition d'un aspect avec Spring AOP peut se faire grâce à une déclaration dans le fichier de configuration du contexte ou grâce aux annotations d'AspectJ.

Durant l'injection des dépendances, le conteneur Spring va créer un proxy dynamique pour l'interface concernée et c'est ce proxy qui sera injecté. Ce proxy est en charge d'exécuter le code des greffons lors de l'invocation des méthodes concernées de l'interface. Un des avantages des aspects est qu'ils sont facilement activables/désactivables : les fonctionnalités qu'ils contiennent peuvent alors être activées ou non sans modifier les classes greffées.

Spring 2.0 facilite la configuration d'AOP en proposant un schéma et un espace de nommage associé dédiés.

Spring AOP utilise des proxys ce qui ne nécessite pas d'outils particulier comme c'est le cas avec AspectJ pour réaliser le tissage (classloader ou compilateur dédié). Spring permet une exécution des advices sur une instance précise alors qu'avec AspectJ l'advice sera exécuté pour toutes les instances puisque la définition est faite sur le type.

 

85.1.1. Les différents types d'advices

Le but de Spring AOP n'est pas de proposer un support complet des fonctionnalités de l'AOP mais de proposer la possibilité de mettre en oeuvre des fonctionnalités transverses qui s'intègrent dans le conteneur Spring. Ainsi, Spring AOP ne propose qu'un support des points de jonction de type exécution de méthodes. Pour la mise en oeuvre d'autres types de points de jonction, il faut utiliser une solution qui propose leur support comme AspectJ.

Spring AOP propose 5 types d'advices :

  • before : le code de l'advice est exécuté avant l'exécution de la méthode. Il n'est pas possible d'inhiber l'invocation de la méthode sauf si une exception est levée dans l'advice
  • after returning : le code de l'aspect est exécuté après l'exécution de la méthode qui renvoie une valeur de retour (aucune exception n'est levée)
  • after throwing : le code de l'aspect est exécuté lorsqu'une exception est levée suite à l'invocation de la méthode
  • after : le code de l'aspect est exécuté après l'exécution de la méthode, même si une exception est levée.
  • around : le code de l'aspect permet de lancer l'exécution de la méthode et ainsi de réaliser des traitements avant, pour par exemple conditionner l'invocation de la méthode et des traitements après

Il est recommandé d'utiliser l'advice le plus adapté au besoin plutôt que de tout faire avec un advice de type around : cette bonne pratique permet de simplifier le code et d'éviter des erreurs potentielles.

Les paramètres des advices sont fortement typés.

 

85.1.2. Spring AOP sans les annotations AspectJ

Sans utiliser les annotations AspectJ, il est possible de mettre en oeuvre Spring AOP en utilisant le fichier de configuration du contexte pour déclarer et configurer les aspects.

L'exemple de cette section va développer un service sur lequel l'invocation des méthodes va être tracée grâce à un aspect. Cet aspect trace les invocations des méthodes des services en affichant les paramètres d'invocation et la valeur de retour.

Les fonctionnalités du service sont définies par une interface.

Exemple :
package com.jmdoudoux.test.spring.service;

import com.jmdoudoux.test.spring.entite.Personne;

public interface PersonneService {
  void afficher();

  void ajouter(Personne personne);
}

L'implémentation du service est volontairement basique.

Exemple :
package com.jmdoudoux.test.spring.service;

import com.jmdoudoux.test.spring.entite.Personne;

public class PersonneServiceImpl implements PersonneService {

  @Override
  public void afficher() {
    try {
      Thread.sleep(250);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }

  @Override
  public void ajouter(final Personne personne) {
    try {
      Thread.sleep(500);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
}

 

85.1.2.1. La définition de l'aspect

Les traitements de l'aspect vont simplement tracer l'invocation d'une méthode avec les paramètres utilisés, invoquer la méthode et tracer la fin de l'invocation.

Exemple :
package com.jmdoudoux.test.spring.aspect;

import org.apache.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;

public class TraceInvocation {

  private static Logger LOGGER = Logger.getLogger(TraceInvocation.class);
  private int order;

  public Object afficherTrace(final ProceedingJoinPoint joinpoint)
      throws Throwable {
    String nomMethode = joinpoint.getTarget().getClass().getSimpleName() + "."
        + joinpoint.getSignature().getName();

    final Object[] args = joinpoint.getArgs();
    final StringBuffer sb = new StringBuffer();
    sb.append(joinpoint.getSignature().toString());
    sb.append(" avec les parametres : (");

    for (int i = 0; i < args.length; i++) {
      sb.append(args[i]);
      if (i < args.length - 1) {
        sb.append(", ");
      }
    }
    sb.append(")");
    LOGGER.info("Debut methode : " + sb);
    try {
      Object obj = joinpoint.proceed();
    } finally {
      LOGGER.info("Fin methode : " + nomMethode + " retour=" + obj);
    }
    return obj;
  }
}

Le code de l'aspect à exécuter doit être contenu dans une méthode qui attend en paramètre un objet de type ProceedingJoinPoint. La classe contenant la méthode doit être instanciable par le contexte.

La classe ProceedingJoinPoint d'AspectJ est utilisée pour obtenir des informations sur le point de jonction et invoquer les traitements qui lui sont associés en utilisant la méthode proceed().

La déclaration et la configuration des aspects se font dans le fichier de configuration.

L'espace de nommage aop permet la déclaration de la configuration de l'AOP notamment en proposant les tags pour configurer les aspects, les points de coupe et les advices.

L'aspect fait référence à un bean géré par le conteneur.

La définition du point de coupe utilise la syntaxe d'AspectJ.

L'advice est une association entre le point de coupe et la méthode de l'aspect à exécuter. Cinq types d'advices sont utilisables : before, after returning, after throwing, after et around.

 

85.1.2.2. La déclaration de l'aspect

Spring 2.0 permet la déclaration des aspects dans la configuration de son contexte qui utilise la syntaxe d'AspectJ pour les définitions des pointcuts. Dans ce cas, l'aspect n'a pas besoin d'être annoté : c'est un simple bean qui doit être déclaré dans la configuration.

Exemple :
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
  <context:component-scan base-package="com.jmdoudoux.test.spring" />
  <aop:config>
    <aop:aspect id="traceInvocationAspect" ref="tracerInvocation">
      <aop:pointcut id="traceInvocationPointcut"
        expression="execution(* com.jmdoudoux.test.spring.service.*ServiceImpl.*(..))" />
      <aop:around pointcut-ref="traceInvocationPointcut" method="afficherTrace" />
    </aop:aspect>
  </aop:config>
  <bean id="tracerInvocation" class="com.jmdoudoux.test.spring.aspect.TraceInvocation"/>
  <bean id="personneService" class="com.jmdoudoux.test.spring.service.PersonneServiceImpl" />
</beans>

Il faut déclarer dans la configuration le bean qui contient le code de l'aspect à exécuter.

La configuration de l'AOP se fait avec un tag <aop:config>.

Chaque aspect est défini grâce à un tag <aop:aspect> : l'attribut ref permet de préciser l'identifiant du bean qui contient les traitements de l'aspect.

Le point de coupe est défini en utilisant un tag <aop:pointcut>. Son attribut expression permet de définir les méthodes concernées en utilisant une expression régulière.

La définition des points de jonction se fait en utilisant des expressions régulières pour définir les méthodes concernées. Plusieurs caractères particuliers peuvent être utilisés pour définir un filtre sur les classes et la signature des méthodes :

  • Le caractère « * » indique n'importe quel caractère sauf le caractère * lui-même ou n'importe quel élément (modificateur, type de retour, classe, méthode, paramètre)
  • Les caractères « .. » indiquent n'importe quelle signature ou sous-package
  • Le caractère « + » indique n'importe quel sous-type

Exemple :

public * *(..) : toutes les méthodes public

* get*(..) : toutes les méthodes commençant par get

* com.jmdoudoux.test.spring.service.IMonService.*(..)) : toutes les méthodes de l'interface IMonService

* com. jmdoudoux.test.spring.service.*.*(..)) : toutes les méthodes du package com. jmdoudoux.test.spring.service

* com. jmdoudoux.test.spring.service..*.*(..)) : toutes les méthodes du package com. jmdoudoux.test.spring.service et de ses sous-packages

 

85.1.2.3. Le namespace aop

Pour pouvoir être utilisé, le namespace aop doit être déclaré dans le tag racine du fichier de définition du contexte.

Exemple :
<beans xmlns="http://www.springframework.org/schema/beans" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:aop="http://www.springframework.org/schema/aop" 
xsi:schemaLocation="http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans.xsd 
http://www.springframework.org/schema/aop 
http://www.springframework.org/schema/aop/spring-aop.xsd">
</beans> 

Le schéma AOP propose plusieurs tags pour permettre la définition des aspects dans le fichier de configuration du contexte :

  • <aop:config> : configure Spring AOP
  • <aop:advisor> : définit un advisor
  • <aop:pointcut> : définit un point de coupe avec une expression régulière
  • <aop:aspectj-autoproxy> : permet d'activer le support des annotations AspectJ pour la création des proxys
  • <aop:scoped-proxy> : crée un proxy pour un bean
  • <aop:spring-configured> : permet d'activer le support de l'annotation @Configurable

Le tag <aop:config> permet dans le fichier de définition du contexte de configurer Spring AOP. Il peut notamment contenir la définition des points de coupe, des advisors et des aspects. Il est possible d'utiliser plusieurs tags <aop:config> dans un même fichier de configuration. L'ordre de déclaration des points de coupe, des advisors et des aspects doit être respecté à l'intérieur d'un tag <aop:config>.

Il est possible de définir un ou plusieurs points de coupe, chacun étant identifié par un nom unique en utilisant le tag <aop:pointcut>. Le nom est fourni en utilisant l'attribut id. L'attribut expression permet de définir une expression régulière qui va définir le point de coupe. La syntaxe de cette expression est identique à celle utilisée avec les annotations AspectJ. Le tag <aop:pointcut> peut être utilisé comme tags fils du tag <aop:config> ou <aop:aspect>.

La définition d'un advice se fait en utilisant un tag dédié pour chaque advice supporté par Spring AOP (before, after returning, after throwing, after, et around). Ces tags sont à utiliser en tant que tag fils du tag <aop:aspect>.

Le tag <aop:before> permet de définir un advice de type before : cet advice permet d'exécuter une méthode de la classe qui encapsule les traitements de l'aspect juste avant l'exécution des méthodes qui correspondent au point de coupe.

Exemple :
  <aop:config>
    <aop:aspect id="traceInvocationAspect" ref="tracerInvocation">
      <aop:pointcut id="traceInvocationPointcut"
        expression="execution(* com.jmdoudoux.test.spring.service.*ServiceImpl.*(..))" />
      <aop:before pointcut-ref="traceInvocationPointcut"
        method="afficherDebutTrace" />
    </aop:aspect>
  </aop:config>

  <bean id="tracerInvocation" class="com.jmdoudoux.test.spring.aspect.TraceInvocation" />

L'attribut pointcut permet de fournir une expression régulière qui précise le point de coupe.

L'attribut pointcut-ref permet de fournir l'identifiant du point de coupe préalablement défini.

L'attribut method permet de préciser le nom de la méthode de la classe encapsulant les traitements de l'aspect qui sera exécutée. Un paramètre de type org.aspectj.lang.JoinPoint dans la signature de la méthode permet d'obtenir des informations sur le point de jonction.

Exemple :
package com.jmdoudoux.test.spring.aspect;

import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;

public class TraceInvocation {

  private static Logger LOGGER = Logger.getLogger(TraceInvocation.class);

  public void afficherDebutTrace(final JoinPoint joinpoint) throws Throwable {
    final Object[] args = joinpoint.getArgs();
    final StringBuffer sb = new StringBuffer();
    sb.append(joinpoint.getSignature().toString());
    sb.append(" avec les parametres : (");

    for (int i = 0; i < args.length; i++) {
      sb.append(args[i]);
      if (i < args.length - 1) {
        sb.append(", ");
      }
    }
    sb.append(")");

    LOGGER.info("Debut methode : " + sb);
  }
}

Le tag <aop:after-returning> définit un advice de type after returning : cet advice permet d'exécuter une méthode de la classe encapsulant les traitements de l'aspect après l'exécution sans qu'une exception soit levée des méthodes qui correspondent au point de coupe.

Exemple :
  <aop:config>
    <aop:aspect id="traceInvocationAspect" ref="tracerInvocation">
      <aop:pointcut id="traceInvocationPointcut"
        expression="execution(* com.jmdoudoux.test.spring.service.*ServiceImpl.*(..))" />
      <aop:after-returning pointcut-ref="traceInvocationPointcut"
        method="afficherFinNormaleTrace" returning="result" />
    </aop:aspect>
  </aop:config>

  <bean id="tracerInvocation" class="com.jmdoudoux.test.spring.aspect.TraceInvocation" />

L'attribut pointcut-ref permet de fournir l'identifiant du point de coupe préalablement défini.

L'attribut method permet de préciser le nom de la méthode de la classe encapsulant les traitements de l'aspect qui sera exécutée.

L'attribut returning permet de préciser le nom du paramètre de la méthode qui va contenir la valeur de retour de l'exécution de la méthode. Dans ce cas, la méthode de l'aspect doit avoir un paramètre dont le type est identique à celui des valeurs de retour des méthodes du point de coupe. Le nom de ce paramètre doit correspondre à celui fourni dans l'attribut returning. La méthode de l'aspect peut aussi avoir un paramètre optionnel de type org.aspectj.lang.JoinPoint.StaticPart qui permet d'obtenir des informations sur le point de jonction.

Exemple :
package com.jmdoudoux.test.spring.aspect;

import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint.StaticPart;

public class TraceInvocation {

  private static Logger LOGGER = Logger.getLogger(TraceInvocation.class);

  public void afficherFinNormaleTrace(final StaticPart staticPart, final Object result)
      throws Throwable {
    String nomMethode = staticPart.getSignature().toLongString();
    LOGGER.info("Fin methode :  " + nomMethode + " retour=" + result);
  }
}

Le tag <aop:after-throwing> permet de définir un advice de type after throwing : cet advice permet d'invoquer une méthode de la classe qui encapsule les traitements de l'aspect après l'exécution ayant levé une exception des méthodes qui correspondent au point de coupe.

Exemple :
  <aop:config>
    <aop:aspect id="traceInvocationAspect" ref="tracerInvocation">
      <aop:pointcut id="traceInvocationPointcut"
        expression="execution(* com.jmdoudoux.test.spring.service.*ServiceImpl.*(..))" />
      <aop:after-throwing pointcut-ref="traceInvocationPointcut"
        method="afficherExceptionTrace" throwing="exception" />
    </aop:aspect>
  </aop:config>

  <bean id="tracerInvocation" class="com.jmdoudoux.test.spring.aspect.TraceInvocation" />

L'attribut pointcut-ref permet de fournir l'identifiant du point de coupe préalablement défini.

L'attribut method permet de préciser le nom de la méthode de la classe encapsulant les traitements de l'aspect qui sera exécutée.

L'attribut throwing permet de préciser le nom du paramètre de la méthode qui va contenir l'exception levée durant l'exécution. Dans ce cas, la méthode de l'aspect doit avoir un paramètre du type Exception à traiter pour les méthodes du point de coupe dont le nom correspond à celui fourni dans l'attribut throwing. La méthode de l'aspect peut aussi avoir un paramètre optionnel de type org.aspectj.lang.JoinPoint.StaticPart qui permet d'obtenir des informations sur le point de jonction.

Exemple :
package com.jmdoudoux.test.spring.aspect;

import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint.StaticPart;

public class TraceInvocation {

  private static Logger LOGGER = Logger.getLogger(TraceInvocation.class);

  public void afficherExceptionTrace(final StaticPart staticPart,
      final Exception exception) throws Throwable {
    String nomMethode = staticPart.getSignature().toLongString();
    LOGGER.error("Exception durant la methode :  " + nomMethode, exception);
  }
}

Le tag <aop:after> permet de définir un advice de type after : cet advice permet d'invoquer une méthode de la classe qui encapsule les traitements de l'aspect après l'exécution des méthodes qui correspondent au point de coupe qu'une exception soit levée ou non durant leur exécution.

Exemple :
  <aop:config>
    <aop:aspect id="traceInvocationAspect" ref="tracerInvocation">
      <aop:pointcut id="traceInvocationPointcut"
        expression="execution(* com.jmdoudoux.test.spring.service.*ServiceImpl.*(..))" />
      <aop:after pointcut-ref="traceInvocationPointcut"
        method="afficherFinTrace" />
    </aop:aspect>
  </aop:config>

  <bean id="tracerInvocation" class="com.jmdoudoux.test.spring.aspect.TraceInvocation" />

L'attribut pointcut-ref permet de fournir l'identifiant du point de coupe préalablement défini.

L'attribut method permet de préciser le nom de la méthode de la classe encapsulant les traitements de l'aspect qui sera exécutée. Un paramètre de type org.aspectj.lang.JoinPoint dans la signature de la méthode permet d'obtenir des informations sur le point de jonction.

Exemple :
package com.jmdoudoux.test.spring.aspect;

import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint.StaticPart;

public class TraceInvocation {

  private static Logger LOGGER = Logger.getLogger(TraceInvocation.class);

  public void afficherFinTrace(final JoinPoint joinpoint) throws Throwable {
    final Object[] args = joinpoint.getArgs();
    final StringBuffer sb = new StringBuffer();
    sb.append(joinpoint.getSignature().toString());
    sb.append(" avec les parametres : (");

    for (int i = 0; i < args.length; i++) {
      sb.append(args[i]);
      if (i < args.length - 1) {
        sb.append(", ");
      }
    }
    sb.append(")");

    LOGGER.info("Fin methode : " + sb);
  }
}

Le tag <aop:around> permet de définir un advice de type around : cet advice permet d'invoquer une méthode de la classe qui encapsule les traitements de l'aspect. Cette méthode va permettre de contrôler l'invocation des méthodes qui correspondent au point de coupe qu'une exception soit levée ou non durant leur exécution. Elle permet donc d'exécuter des traitements avant l'invocation, peut conditionner cette invocation et exécuter des traitements suite à cette invocation.

Exemple :
  <aop:config>
    <aop:aspect id="traceInvocationAspect" ref="tracerInvocation">
      <aop:pointcut id="traceInvocationPointcut"
        expression="execution(* com.jmdoudoux.test.spring.service.*ServiceImpl.*(..))" />
      <aop:around pointcut-ref="traceInvocationPointcut"
        method="afficherTrace" />
    </aop:aspect>
  </aop:config>

  <bean id="tracerInvocation" class="com.jmdoudoux.test.spring.aspect.TraceInvocation" />

L'attribut pointcut-ref permet de fournir l'identifiant du point de coupe préalablement défini.

L'attribut method permet de préciser le nom de la méthode de la classe encapsulant les traitements de l'aspect qui sera exécutée. Un paramètre de type org.aspectj.lang.ProceedingJoinPoint dans la signature de la méthode permet d'obtenir des informations sur le point de jonction et de demander l'invocation de la méthode liée au point de jonction en utilisant la méthode proceed().

Exemple :
package com.jmdoudoux.test.spring.aspect;

import org.apache.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;

public class TraceInvocation {

  private static Logger LOGGER = Logger.getLogger(TraceInvocation.class);

  public Object afficherTrace(final ProceedingJoinPoint joinpoint)
      throws Throwable {
    String nomMethode = joinpoint.getTarget().getClass().getSimpleName() + "."
        + joinpoint.getSignature().getName();

    final Object[] args = joinpoint.getArgs();
    final StringBuffer sb = new StringBuffer();
    sb.append(joinpoint.getSignature().toString());
    sb.append(" avec les parametres : (");

    for (int i = 0; i < args.length; i++) {
      sb.append(args[i]);
      if (i < args.length - 1) {
        sb.append(", ");
      }
    }
    sb.append(")");

    LOGGER.info("Debut methode : " + sb);

    try {
      Object obj = joinpoint.proceed();
    } finally {
    LOGGER.info("Fin methode :  " + nomMethode + " retour=" + obj);
    }
    return obj;
  }
}

 

85.1.2.4. Une autre implémentation de l'aspect

Il est aussi possible d'implémenter l'aspect en utilisant deux points de coupe de type before et after-returning.

Le code de l'aspect doit alors avoir deux méthodes, une pour chaque point de coupe avec leurs signatures respectives.

Exemple :
package com.jmdoudoux.test.spring.aspect;

import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.JoinPoint.StaticPart;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.core.Ordered;

public class TraceInvocation implements Ordered {

  private static Logger LOGGER = Logger.getLogger(TraceInvocation.class);
  private int order;

  public void afficherDebutTrace(final JoinPoint joinpoint) throws Throwable {
    final Object[] args = joinpoint.getArgs();
    final StringBuffer sb = new StringBuffer();
    sb.append(joinpoint.getSignature().toString());
    sb.append(" avec les parametres : (");

    for (int i = 0; i < args.length; i++) {
      sb.append(args[i]);
      if (i < args.length - 1) {
        sb.append(", ");
      }
    }
    sb.append(")");

    LOGGER.info("Debut methode : " + sb);
  }

  public void afficherFinTrace(final StaticPart staticPart, final Object result)
      throws Throwable {
    String nomMethode = staticPart.getSignature().toLongString();
    LOGGER.info("Fin methode :  " + nomMethode + " retour=" + result);
  }

  @Override
  public int getOrder() {
    return this.order;
  }

  public void setOrder(final int order) {
    this.order = order;
  }
}

La déclaration de l'aspect dans le fichier de configuration utilise les deux points de coupe.

Exemple :
<?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:aop="http://www.springframework.org/schema/aop"
  xmlns:tx="http://www.springframework.org/schema/tx" 
  xmlns:context="http://www.springframework.org/schema/context"
  xsi:schemaLocation="
http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd 
http://www.springframework.org/schema/aop 
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/context 
http://www.springframework.org/schema/context/spring-context.xsd">

  <context:component-scan base-package="com.jmdoudoux.test.spring" />

  <aop:config>
    <aop:aspect id="monitorerPerfAspect" ref="monitorerPerf">
      <aop:pointcut id="methodeService"
        expression="execution(* com.jmdoudoux.test.spring.service.*ServiceImpl.*(..))" />
      <aop:around method="executer" pointcut-ref="methodeService" />
    </aop:aspect>
    <aop:aspect id="traceInvocationAspect" ref="tracerInvocation">
      <aop:pointcut id="traceInvocationPointcut"
        expression="execution(* com.jmdoudoux.test.spring.service.*ServiceImpl.*(..))" />
      <aop:before pointcut-ref="traceInvocationPointcut"
        method="afficherDebutTrace" />
      <aop:after-returning pointcut-ref="traceInvocationPointcut"
        method="afficherFinTrace" returning="result" />
    </aop:aspect>
  </aop:config>

  <bean id="monitorerPerf" class="com.jmdoudoux.test.spring.aspect.MonitorePerf">
    <property name="order" value="1" />
  </bean>

  <bean id="tracerInvocation" class="com.jmdoudoux.test.spring.aspect.TraceInvocation">
    <property name="order" value="2" />
  </bean>

  <bean id="personneService" class="com.jmdoudoux.test.spring.service.
  PersonneServiceImpl" />

</beans>

L'application de test demande une instance du service à Spring et invoque ses méthodes ajouter() et afficher().

Exemple :
package com.jmdoudoux.test.spring;

import org.apache.log4j.Logger;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.jmdoudoux.test.spring.entite.Personne;
import com.jmdoudoux.test.spring.service.PersonneService;

public class MonApp {

  private static Logger LOGGER = Logger.getLogger(MonApp.class);

  public static void main(final String[] args) throws Exception {

    LOGGER.info("Debut de l'application");

    ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext(
        new String[] { "appContext.xml" });

    PersonneService personneService = (PersonneService) appContext
        .getBean("personneService");

    LOGGER.info("Debut invocation du service");
    try {
      personneService.ajouter(new Personne());
    } catch (Exception e) {
      LOGGER.error("exception " + e.getClass().getName() + " interceptee");
    }

    personneService.afficher();

    LOGGER.info("Fin invocation du service");

    LOGGER.info("Fin de l'application");
  }
} 

Les bibliothèques requises sont : spring-aop 3.0.5, spring-asm 3.0.5, spring-aspect 3.0.5, spring-beans 3.0.5, spring-core 3.0.5, spring-context 3.0.5, spring-expression 3.0.5, aspectjrt 1.6.8, aspectjweaver 1.6.8, aopalliance 1.0, commons-logging 1.1.1, log4j 1.2.16

La bibliothèque aspectjrt est requise car certaines classes sont utilisées lors de la mise en oeuvre de Spring AOP notamment dans le code de l'aspect.

Résultat :
2011-07-03 19:07:31,671  INFO [com.jmdoudoux.test.spring.MonApp] Debut de l'application
2011-07-03 19:07:32,718  INFO [com.jmdoudoux.test.spring.MonApp] Debut invocation du service
2011-07-03 19:07:32,718  INFO [com.jmdoudoux.test.spring.aspect.TraceInvocation] Debut methode
 : void com.jmdoudoux.test.spring.service.PersonneService.ajouter(Personne) avec les
parametres : (com.jmdoudoux.test.spring.entite.Personne@26d607)
2011-07-03 19:07:33,218  INFO [com.jmdoudoux.test.spring.aspect.TraceInvocation] Fin methode :
public abstract void com.jmdoudoux.test.spring.service.PersonneService.ajouter(
com.jmdoudoux.test.spring.entite.Personne)
retour=null
2011-07-03 19:07:33,218  INFO [com.jmdoudoux.test.spring.aspect.TraceInvocation] Debut methode
 : void com.jmdoudoux.test.spring.service.PersonneService.afficher() avec les parametres : ()
2011-07-03 19:07:33,468  INFO [com.jmdoudoux.test.spring.aspect.TraceInvocation] Fin methode :
public abstract void com.jmdoudoux.test.spring.service.PersonneService.afficher() retour=null
2011-07-03 19:07:33,468  INFO [com.jmdoudoux.test.spring.MonApp] Fin invocation du service
2011-07-03 19:07:33,468  INFO [com.jmdoudoux.test.spring.MonApp] Fin de l'application

 

85.1.2.5. La gestion de l'ordre des aspects

Il est possible de définir plusieurs aspects sur un même point de coupe. Dans ce cas, il peut être nécessaire de définir l'ordre d'exécution des aspects.

L'exemple de cette section va définir un aspect pour mesurer le temps d'exécution d'une méthode qui sera invoquée au même endroit que l'aspect qui trace les invocations.

Les aspects doivent alors implémenter l'interface Ordered qui ne définit qu'une seule méthode getOrder() renvoyant un entier.

Le plus simple est de définir un setter sur un champ order, ce qui va permettre de configurer la valeur du numéro d'ordre dans la configuration du contexte.

Exemple :
package com.jmdoudoux.test.spring.aspect;

import org.apache.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.core.Ordered;
import org.springframework.util.StopWatch;

public class MonitorePerf implements Ordered {
  private static Logger LOGGER = Logger.getLogger(MonitorePerf.class);
  private int order;

  public Object executer(final ProceedingJoinPoint joinpoint) throws Throwable {
    Object returnValue;
    StopWatch clock = new StopWatch(getClass().getName());
    try {
      clock.start(joinpoint.toString());
      returnValue = joinpoint.proceed();
    } finally {
      clock.stop();
      LOGGER.info("temps d'execution : " + clock.prettyPrint());
    }
    return returnValue;
  }

  @Override
  public int getOrder() {
    return this.order;
  }

  public void setOrder(final int order) {
    this.order = order;
  }
}

Dans le fichier de configuration du contexte, le second aspect est défini et l'ordre est précisé pour les deux aspects en utilisant leur propriété order.

Exemple :
<?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:aop="http://www.springframework.org/schema/aop"
  xmlns:tx="http://www.springframework.org/schema/tx" 
  xmlns:context="http://www.springframework.org/schema/context"
  xsi:schemaLocation="
http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd 
http://www.springframework.org/schema/aop 
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/context 
http://www.springframework.org/schema/context/spring-context.xsd">

  <context:component-scan base-package="com.jmdoudoux.test.spring" />

  <aop:config>
    <aop:aspect id="monitorerPerfAspect" ref="monitorerPerf">
      <aop:pointcut id="methodeService"
        expression="execution(* com.jmdoudoux.test.spring.service.*ServiceImpl.*(..))" />
      <aop:around method="executer" pointcut-ref="methodeService" />
    </aop:aspect>
    <aop:aspect id="traceInvocationAspect" ref="tracerInvocation">
      <aop:pointcut id="traceInvocationPointcut"
        expression="execution(* com.jmdoudoux.test.spring.service.*ServiceImpl.*(..))" />
      <aop:around pointcut-ref="traceInvocationPointcut" method="afficherTrace" />
    </aop:aspect>

  </aop:config>

  <bean id="monitorerPerf" class="com.jmdoudoux.test.spring.aspect.MonitorePerf">
    <property name="order" value="1" />
  </bean>

  <bean id="tracerInvocation" class="com.jmdoudoux.test.spring.aspect.TraceInvocation">
    <property name="order" value="2" />
  </bean>

  <bean id="personneService" class="com.jmdoudoux.test.spring.service.PersonneServiceImpl" />

</beans>

Lors de l'exécution de l'exemple, les aspects sont invoqués dans l'ordre précisé.

Résultat :
2011-06-26 17:48:40,890  INFO [com.jmdoudoux.test.spring.MonApp] Debut de
 l'application
2011-06-26 17:48:41,921  INFO [com.jmdoudoux.test.spring.MonApp] Debut 
invocation du service
2011-06-26 17:48:41,921  INFO [com.jmdoudoux.test.spring.aspect.TraceInvocation] 
Debut methode : void com.jmdoudoux.test.spring.service.PersonneService.ajouter(
Personne) avec les parametres : (com.jmdoudoux.test.spring.entite.Personne@419d05)
2011-06-26 17:48:42,421  INFO [com.jmdoudoux.test.spring.aspect.TraceInvocation] 
Fin methode :  PersonneServiceImpl.ajouter retour=null
2011-06-26 17:48:42,421  INFO [com.jmdoudoux.test.spring.aspect.MonitorePerf] 
temps d'execution : StopWatch 'com.jmdoudoux.test.spring.aspect.MonitorePerf': 
running time (millis) = 500
-----------------------------------------
ms     %     Task name
-----------------------------------------
00500  100 %  execution(void com.jmdoudoux.test.spring.service.PersonneService.
ajouter(Personne))

2011-06-26 17:48:42,421  INFO [com.jmdoudoux.test.spring.aspect.TraceInvocation] 
Debut methode : void com.jmdoudoux.test.spring.service.PersonneService.afficher(
) avec les parametres : ()
2011-06-26 17:48:42,671  INFO [com.jmdoudoux.test.spring.aspect.TraceInvocation] 
Fin methode :  PersonneServiceImpl.afficher retour=null
2011-06-26 17:48:42,671  INFO [com.jmdoudoux.test.spring.aspect.MonitorePerf] 
temps d'execution : StopWatch 'com.jmdoudoux.test.spring.aspect.MonitorePerf': 
running time (millis) = 250
-----------------------------------------
ms     %     Task name
-----------------------------------------
00250  100 %  execution(void com.jmdoudoux.test.spring.service.PersonneService.
afficher())

2011-06-26 17:48:42,671  INFO [com.jmdoudoux.test.spring.MonApp] Fin invocation 
du service
2011-06-26 17:48:42,671  INFO [com.jmdoudoux.test.spring.MonApp] Fin de 
l'application

Il est possible de simplifier encore plus le fichier de configuration en utilisant les annotations pour définir les beans et les aspects puisque les aspects sont aussi des beans.

 

85.1.3. Spring AOP avec les annotations AspectJ

La mise en oeuvre de Spring AOP peut se faire en utilisant les annotations d'AspectJ pour réaliser sa définition. Bien que ce soit les annotations d'AspectJ qui sont utilisées, le tissage ne va pas être réalisé avec AspectJ mais avec Spring AOP.

L'utilisation des annotations d'AspectJ requiert un Java SE 5 ou ultérieur.

La classe qui contient les traitements de l'aspect utilise les annotations d'AspectJ pour définir l'aspect, le point de coupe et les points de jonction. La configuration est dans ce cas simplifiée.

La classe de l'aspect doit être annotée avec @Aspect.

Exemple :
package com.jmdoudoux.test.spring.aspect;

import org.apache.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class TraceInvocation {

  private static Logger LOGGER = Logger.getLogger(TraceInvocation.class);

  @Around("traceInvocationPointcut()")
  public Object afficherTrace(final ProceedingJoinPoint joinpoint)
      throws Throwable {
    String nomMethode = joinpoint.getTarget().getClass().getSimpleName() + "."
        + joinpoint.getSignature().getName();

    final Object[] args = joinpoint.getArgs();
    final StringBuffer sb = new StringBuffer();
    sb.append(joinpoint.getSignature().toString());
    sb.append(" avec les parametres : (");

    for (int i = 0; i < args.length; i++) {
      sb.append(args[i]);
      if (i < args.length - 1) {
        sb.append(", ");
      }
    }
    sb.append(")");

    LOGGER.info("Debut methode : " + sb);

    try {
      Object obj = joinpoint.proceed();
    } finally { 
      LOGGER.info("Fin methode :  " + nomMethode + " retour=" + obj);
    }
    return obj;
  }

  @Pointcut("execution(* com.jmdoudoux.test.spring.service.*ServiceImpl.*(..))")
  public void traceInvocationPointcut() {
  }
}

Le code de l'aspect à exécuter doit être contenu dans une méthode dont la signature est particulière et dépend de l'annotation utilisée pour préciser son point de jonction. Dans l'exemple ci-dessus, elle attend en paramètre un objet de type ProceedingJoinPoint puisque l'annotation utilisée est @Around.

L'annotation @Pointcut permet de définir des points de coupe. Elle s'utilise sur une méthode sans traitement d'une classe ou d'une interface annotée avec @Aspect. Cette classe ou interface peut avoir plusieurs méthodes annotées avec @Pointcut.

Exemple :
package com.jmdoudoux.test.spring.aspect;

@Aspect
public interface ITraceInvocation {
  @Pointcut("execution(* com.jmdoudoux.test.spring.service.*ServiceImpl.*(..))")
  void traceInvocationPointcut();
}

Comme la définition de l'aspect est faite avec des annotations, le fichier de configuration est grandement simplifié.

Pour utiliser des aspects définis avec les annotations d'AspectJ par Spring AOP, il faut utiliser le tag <aop:aspectj-autoproxy> dans le fichier de configuration. La classe qui encapsule l'aspect doit aussi être définie dans la configuration du contexte.

Exemple :
<?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:aop="http://www.springframework.org/schema/aop"
 xmlns:tx="http://www.springframework.org/schema/tx" 
  xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
  <aop:aspectj-autoproxy/>
  <bean id="tracerInvocation" class="com.jmdoudoux.test.spring.aspect.TraceInvocation">
  </bean>
  <bean id="personneService" class="com.jmdoudoux.test.spring.service.PersonneServiceImpl" />
</beans>

Remarque : bien que les annotations d'AspectJ soient utilisées, le tissage n'est pas réalisé par AspectJ mais par Spring AOP en créant dynamiquement des proxys.

Les bibliothèques requises sont : spring-aop 3.0.5, spring-asm 3.0.5, spring-aspect 3.0.5, spring-beans 3.0.5, spring-core 3.0.5, spring-context 3.0.5, spring-expression 3.0.5, aspectjrt 1.6.8, aspectjweaver 1.6.8, aopalliance 1.0, commons-logging 1.1.1, log4j 1.2.16

Il est possible de simplifier encore plus le fichier de définition du contexte en déclarant les beans du service et des aspects grâce aux annotations. Pour cela, il faut permettre au conteneur Spring de détecter automatiquement ces beans, même les aspects, en les annotant avec @Component. Il est très important que le bean de l'aspect soit déclaré dans le contexte pour permettre à Spring AOP de créer le proxy requis : si l'aspect n'est pas annoté avec @Component, l'aspect ne sera tout simplement pas exécuté.

Exemple :
package com.jmdoudoux.test.spring.aspect;
      
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.JoinPoint.StaticPart;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
@Component
public class TraceInvocation {
  private static Logger LOGGER = Logger.getLogger(TraceInvocation.class);
  public void afficherDebutTrace(final JoinPoint joinpoint) throws Throwable {
    final Object[] args = joinpoint.getArgs();
    final StringBuffer sb = new StringBuffer();
   
    sb.append(joinpoint.getSignature().toString());
    sb.append(" avec les parametres : (");
    for (int i = 0; i < args.length; i++) {
      sb.append(args[i]);
      if (i < args.length - 1) {
        sb.append(", ");
      }
    }
    sb.append(")");
   
    LOGGER.info("Debut methode : " + sb);
  }
  
  public void afficherFinTrace(final StaticPart staticPart, final Object result)
      throws  Throwable {
    String nomMethode = staticPart.getSignature().toLongString();
   
    LOGGER.info("Fin methode : " + nomMethode + " retour=" + result);
  }
 
  @Around("traceInvocationPointcut()")
  public Object afficherTrace(final ProceedingJoinPoint joinpoint)
      throws Throwable {
    String nomMethode = joinpoint.getTarget().getClass().getSimpleName() + "."
        +  joinpoint.getSignature().getName();
    final Object[] args = joinpoint.getArgs();
    final StringBuffer sb = new StringBuffer();
   
    sb.append(joinpoint.getSignature().toString());
    sb.append(" avec les parametres : (");
    for (int i = 0; i < args.length; i++) {
      sb.append(args[i]);
      if (i < args.length - 1) {
        sb.append(", ");
      }
    }
   
    sb.append(")");
   
    LOGGER.info("Debut methode : " + sb);
    try {
      Object obj = joinpoint.proceed();
    } finally {
      LOGGER.info("Fin methode :  " + nomMethode + " retour=" + obj);
    }
    return obj;
  }
 
  @Pointcut("execution(* com.jmdoudoux.test.spring.service.*ServiceImpl.*(..))")
  public void traceInvocationPointcut() {
  }
}

Le fichier de configuration est alors minimaliste.

Exemple :
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop 
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/context 
http://www.springframework.org/schema/context/spring-context.xsd">
  
  <context:component-scan base-package="com.jmdoudoux.test.spring" />
  <aop:aspectj-autoproxy/>
  
</beans>

Le résultat de l'exécution est le même.

Il est possible d'implémenter l'aspect en utilisant deux points de coupe de types before et after-returning et leurs annotations respectives.

Le code de l'aspect doit alors avoir deux méthodes, une pour chaque point de coupe avec leurs signatures respectives.

Exemple :
package com.jmdoudoux.test.spring.aspect;

import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.JoinPoint.StaticPart;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class TraceInvocation {
  private static Logger LOGGER = Logger.getLogger(TraceInvocation.class);
 
  @Before("traceInvocationPointcut()")
  public void afficherDebutTrace(final JoinPoint joinpoint) throws Throwable {
    final Object[] args = joinpoint.getArgs();
    final StringBuffer sb = new StringBuffer();
    sb.append(joinpoint.getSignature().toString());
    sb.append(" avec les parametres : (");
    for (int i = 0; i < args.length; i++) {
      sb.append(args[i]);
      if (i < args.length - 1) {
        sb.append(", ");
      }
    }
   
    sb.append(")");
   
    LOGGER.info("Debut methode : " + sb);
  }
 
  @AfterReturning(pointcut = "traceInvocationPointcut()", returning = "result")
  public void afficherFinTrace(final StaticPart staticPart, final Object result)
      throws  Throwable {
    String nomMethode = staticPart.getSignature().toLongString();
   
    LOGGER.info("Fin methode : " + nomMethode + " retour=" + result);
  }
 
  @Pointcut("execution(* com.jmdoudoux.test.spring.service.*ServiceImpl.*(..))")
  public void traceInvocationPointcut() {
  }
}

Attention : toutes les fonctionnalités d'AspectJ ne sont pas prises en charge par Spring AOP. Une exception est levée si Spring AOP rencontre une fonctionnalité non supportée.

Résultat :
Caused by:
java.lang.IllegalArgumentException: DeclarePrecendence not presently supported
in Spring AOP

 

85.1.3.1. La gestion de l'ordre des aspects

La gestion de l'ordre des aspects définis avec les annotations AspectJ se fait de la même façon que pour les aspects définis dans la configuration du contexte puisqu'au final dans les deux cas, c'est Spring AOP qui prend en charge les aspects. Il faut aussi utiliser l'interface Ordered qui possède une seule méthode getOrder(). Cette méthode getOrder() doit renvoyer le numéro d'ordre d'exécution de l'aspect.

Exemple :
package com.jmdoudoux.test.spring.aspect;
      
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.JoinPoint.StaticPart;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class TraceInvocation implements Ordered {
  private static Logger LOGGER = Logger.getLogger(TraceInvocation.class);
  private int order;
 
  @Around("traceInvocationPointcut()")
  public Object afficherTrace(final ProceedingJoinPoint joinpoint)
      throws Throwable {
    String nomMethode = joinpoint.getTarget().getClass().getSimpleName() + "."
        + joinpoint.getSignature().getName();
    final Object[] args = joinpoint.getArgs();
    final StringBuffer sb = new StringBuffer();
   
    sb.append(joinpoint.getSignature().toString());
    sb.append(" avec les parametres : (");
    for (int i = 0; i < args.length; i++) {
      sb.append(args[i]);
      if (i < args.length - 1) {
        sb.append(", ");
      }
    }
    sb.append(")");
    LOGGER.info("Debut methode : " + sb);
    try {
      Object obj = joinpoint.proceed();
    }
    finally {    
      LOGGER.info("Fin methode :  " + nomMethode + " retour=" + obj);
    }
    return obj;
  }
  
  @Override
  public int getOrder() {
    return order;
  }
  
  @Value("2")
  public void setOrder(final int order) {
    this.order = order;
  }
  @Pointcut("execution(* com.jmdoudoux.test.spring.service.*ServiceImpl.*(..))")
  public void traceInvocationPointcut() {
  }
}

Le second aspect est défini avec son propre numéro d'ordre d'invocation.

Exemple :
package com.jmdoudoux.test.spring.aspect;
      
import org.apache.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;

@Component
@Aspect
public class MonitorePerf implements Ordered {
  private static Logger LOGGER = Logger.getLogger(MonitorePerf.class);
  private int order;
 
  @Around("monitorePerfPointcut()")
  public Object executer(final ProceedingJoinPoint joinpoint) throws Throwable {
    Object returnValue;
    StopWatch clock = new StopWatch(getClass().getName());
    try {
      clock.start(joinpoint.toString());
      returnValue = joinpoint.proceed();
    } finally {
      clock.stop();
      LOGGER.info("temps d'execution : " + clock.prettyPrint());
    }
    return returnValue;
  }
  
  @Override
  public int getOrder() {
    return order;
  }
 
  @Pointcut("execution(* com.jmdoudoux.test.spring.service.*ServiceImpl.*(..))")
  public void monitorePerfPointcut() {
  }
 
  @Value("1")
  public void setOrder(final int order) {
    this.order = order;
  }
}

Il faut être attentif au numéro d'ordre attribué à chaque aspect selon le type d'advice utilisé et le résultat souhaité. Dans l'exemple ci-dessus, le but est d'avoir dans les logs les traces d'exécution suivies des informations sur le temps d'exécution. C'est pourtant l'aspect de monitoring qui possède le numéro d'ordre d'exécution 1 puisque l'aspect utilise l'advice around.

Comme toute la configuration est faite avec des annotations, le fichier de définition du contexte est toujours aussi simple.

Exemple :
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop 
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/context 
http://www.springframework.org/schema/context/spring-context.xsd">

  <context:component-scan base-package="com.jmdoudoux.test.spring" />
  <aop:aspectj-autoproxy/>
  
</beans>

Lors de l'exécution, l'ordre est respecté.

Résultat :
2011-07-10 16:49:25,546  INFO [com.jmdoudoux.test.spring.MonApp] Debut de l'application
2011-07-10 16:49:26,546  INFO [com.jmdoudoux.test.spring.MonApp] Debut invocation du service
2011-07-10 16:49:26,578  INFO [com.jmdoudoux.test.spring.aspect.TraceInvocation] 
Debut methode : void com.jmdoudoux.test.spring.service.PersonneService.ajouter(Personne) 
avec les parametres : (com.jmdoudoux.test.spring.entite.Personne@55a338)
2011-07-10 16:49:27,078  INFO
[com.jmdoudoux.test.spring.aspect.TraceInvocation] 
Fin methode :  PersonneServiceImpl.ajouter retour=null
2011-07-10 16:49:27,078  INFO [com.jmdoudoux.test.spring.aspect.MonitorePerf]

temps d'execution :
StopWatch 'com.jmdoudoux.test.spring.aspect.MonitorePerf': 
running time (millis) = 500
-----------------------------------------
ms     %    
Task name
-----------------------------------------
00500  100 % 
execution(void com.jmdoudoux.test.spring.service.PersonneService.
ajouter(Personne))
2011-07-10 16:49:27,078  INFO [com.jmdoudoux.test.spring.aspect.TraceInvocation] 
Debut methode : void com.jmdoudoux.test.spring.service.PersonneService.afficher() 
avec les parametres : ()
2011-07-10 16:49:27,328  INFO [com.jmdoudoux.test.spring.aspect.TraceInvocation] 
Fin methode :  PersonneServiceImpl.afficher retour=null
2011-07-10 16:49:27,328  INFO
[com.jmdoudoux.test.spring.aspect.MonitorePerf] 
temps d'execution :
StopWatch 'com.jmdoudoux.test.spring.aspect.MonitorePerf': 
running time (millis) = 250
-----------------------------------------
ms     %    
Task name
-----------------------------------------
00250  100 % 
execution(void com.jmdoudoux.test.spring.service.PersonneService.afficher())
2011-07-10 16:49:27,328  INFO [com.jmdoudoux.test.spring.MonApp] Fin invocation du service
2011-07-10 16:49:27,328  INFO [com.jmdoudoux.test.spring.MonApp] Fin de l'application

Pour modifier cet ordre, il suffit de changer la valeur de la propriété order. L'ordre d'exécution des aspects est alors inversé.

Résultat :
2011-07-10 16:57:57,703  INFO [com.jmdoudoux.test.spring.MonApp] Debut de l'application
2011-07-10 16:57:58,718  INFO [com.jmdoudoux.test.spring.MonApp] Debut invocation du service
2011-07-10 16:57:58,750  INFO [com.jmdoudoux.test.spring.aspect.TraceInvocation] 
Debut methode : void com.jmdoudoux.test.spring.service.PersonneService.ajouter( Personne) 
avec les parametres : (com.jmdoudoux.test.spring.entite.Personne@ b1074a)
2011-07-10 16:57:59,250  INFO [com.jmdoudoux.test.spring.aspect.MonitorePerf] 
temps d'execution :
StopWatch 'com.jmdoudoux.test.spring.aspect.MonitorePerf': 
running time (millis) = 500
-----------------------------------------
ms     %    
Task name
-----------------------------------------
00500  100 % 
execution(void com.jmdoudoux.test.spring.service.PersonneService.
ajouter(Personne))
2011-07-10 16:57:59,250  INFO [com.jmdoudoux.test.spring.aspect.TraceInvocation] 
Fin methode :  PersonneServiceImpl.ajouter retour=null
2011-07-10 16:57:59,250  INFO [com.jmdoudoux.test.spring.aspect.TraceInvocation] 
Debut methode : void com.jmdoudoux.test.spring.service.PersonneService.afficher() 
avec les parametres : ()
2011-07-10 16:57:59,500  INFO [com.jmdoudoux.test.spring.aspect.MonitorePerf] 
temps d'execution :
StopWatch 'com.jmdoudoux.test.spring.aspect.MonitorePerf': 
running time (millis) = 250
-----------------------------------------
ms     %    
Task name
-----------------------------------------
00250  100 % 
execution(void com.jmdoudoux.test.spring.service.PersonneService.afficher())
2011-07-10 16:57:59,500  INFO [com.jmdoudoux.test.spring.aspect.TraceInvocation]

Fin methode :  PersonneServiceImpl.afficher retour=null
2011-07-10 16:57:59,500  INFO [com.jmdoudoux.test.spring.MonApp] Fin invocation du service
2011-07-10 16:57:59,500  INFO [com.jmdoudoux.test.spring.MonApp] Fin de l'application

 

85.2. AspectJ

Spring permet aussi une utilisation d'AspectJ pour mettre en oeuvre l'AOP : AspectJ propose un support très complet des possibilités offertes par l'AOP.

AspectJ utilise sa propre syntaxe pour la création d'un aspect mais surtout les aspects peuvent être tissés au runtime avec un agent dédié (classloader qui enrichit le bytecode lors de son chargement) ou à la compilation avec le compilateur dédié d'AspectJ.

Exemple :
public aspect HelloAspectJ {

  pointcut methodeMain() : execution(* main(..));

  after() returning : methodMain() { 
    System.out.println("Hello AspectJ!"); 
  }
} 

AspectJ 5 permet la création d'un aspect sous la forme d'une simple classe annotée avec des annotations dédiées comme @Aspect. L'aspect ci-dessus peut ainsi être défini avec l'annotation @Aspect.

Exemple :
@Aspect
public class HelloAspectJ {

  @Pointcut("execution(* main(..))") 
  public void methodeMain() {}

  @AfterReturning("methodeMain()")
  public void saluer() {
    System.out.println("Hello AspectJ!"); 
  }
} 

 

85.2.1. AspectJ avec LTW (Load Time Weaving)

Avec cette solution, le tissage des aspects va être réalisé dynamiquement, aux chargements des classes concernées, par un agent d'AspectJ.

Le code de l'application, du service, du bean et de l'aspect sont les mêmes que dans l'exemple utilisant Spring AOP avec les annotations d'AspectJ. Les différences vont se faire au niveau de la configuration du contexte Spring, des bibliothèques requises et de l'utilisation de l'agent dans la JVM.

Le fichier de configuration du contexte n'a plus besoin de contenir des définitions relatives aux aspects.

Exemple :
<?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-2.5.xsd 
http://www.springframework.org/schema/context 
http://www.springframework.org/schema/context/spring-context-2.5.xsd">

  <context:annotation-config />
  <context:spring-configured />

  <context:component-scan base-package="com.jmdoudoux.test.spring" />

</beans> 

Il faut définir un fichier META-INF/aop.xml accessible par le classpath qui va contenir les informations sur le tissage à réaliser par AspectJ.

Exemple :
<?xml version="1.0" encoding="UTF-8"?>
<aspectj>
  <aspects>
    <aspect name="com.jmdoudoux.test.spring.aspect.MonitorePerf" />
    <aspect name="com.jmdoudoux.test.spring.aspect.TraceInvocation" />
  </aspects>
 
  <weaver options="-XnoInline -Xlint:ignore -verbose -showWeaveInfo">
    <include name="com.jmdoudoux.test.spring.service..*" />
  </weaver>
</aspectj>

Le tag racine de ce fichier de configuration est le tag <aspectj>.

Les aspects doivent être déclarés chacun dans un tag <aspect> fils du tag <aspects>. L'attribut name permet de préciser le nom pleinement qualifié de l'aspect.

Le tag weaver permet de configurer le tissage des aspects. L'attribut options permet de définir les options du tisseur.

Les options de tissage «-verbose » et « -showWeaveInfo » sont particulièrement utiles dans l'environnement de développement pour obtenir des informations sur les opérations réalisées par AspectJ (enregistrement des aspects, leur tissage, ...).

Le tag fils <include> permet de préciser sous la forme d'une expression régulière les classes qui sont concernées par le tissage.

Il faut lancer la JVM avec l'option -javaagent:chemin_vers_aspectjweaver.jar

exemple :

-javaagent:lib/aspectjweaver-1.6.1.jar

Le classpath de l'application contient les bibliothèques : org.springframework.asm-3.0.5.RELEASE.jar, org.springframework.aspects-3.0.5.RELEASE.jar, org.springframework.beans-3.0.5.RELEASE.jar, org.springframework.context-3.0.5.RELEASE.jar, org.springframework.core-3.0.5.RELEASE.jar, org.springframework.expression-3.0.5.RELEASE.jar, org.apache.commons.logging-1.1.1.jar, aspectrt.jar, log4j-1.2.16.jar

Résultat :
2011-08-09 18:57:48,968  INFO [com.jmdoudoux.test.spring.MonApp] Debut de 
l'application
2011-08-09 18:57:49,265  INFO [org.springframework.context.support.
ClassPathXmlApplicationContext] Refreshing org.springframework.context.support.
ClassPathXmlApplicationContext@2d189c: startup date [Tue Aug 09 18:57:49 CEST 
2011]; root of context hierarchy
2011-08-09 18:57:49,578  INFO [org.springframework.beans.factory.xml.
XmlBeanDefinitionReader] Loading XML bean definitions from class path resource [
appContext.xml]
2011-08-09 18:57:50,515  INFO [org.springframework.beans.factory.support.
DefaultListableBeanFactory] Pre-instantiating singletons in org.springframework.
beans.factory.support.DefaultListableBeanFactory@e8a0cd: defining beans [org.
springframework.context.annotation.internalConfigurationAnnotationProcessor,org.
springframework.context.annotation.internalAutowiredAnnotationProcessor,org.
springframework.context.annotation.internalRequiredAnnotationProcessor,org.
springframework.context.annotation.internalCommonAnnotationProcessor,org.
springframework.context.config.internalBeanConfigurerAspect,personne1,personne2,
personneService]; root of factory hierarchy
Invocation constructeur PersonneServiceImpl()
2011-08-09 18:57:50,656  INFO [com.jmdoudoux.test.spring.MonApp] Debut 
invocation du service
2011-08-09 18:57:50,687  INFO [com.jmdoudoux.test.spring.aspect.TraceInvocation] 
Debut methode : void com.jmdoudoux.test.spring.service.PersonneServiceImpl.
ajouter(Personne) avec les parametres : (com.jmdoudoux.test.spring.entite.
Personne@12f195)
2011-08-09 18:57:51,187  INFO [com.jmdoudoux.test.spring.aspect.TraceInvocation] 
Fin methode :  public void com.jmdoudoux.test.spring.service.
PersonneServiceImpl.ajouter(com.jmdoudoux.test.spring.entite.Personne) retour=
null
2011-08-09 18:57:51,187  INFO [com.jmdoudoux.test.spring.aspect.MonitorePerf] 
temps d'execution : StopWatch 'com.jmdoudoux.test.spring.aspect.MonitorePerf': 
running time (millis) = 516
-----------------------------------------
ms     %     Task name
-----------------------------------------
00516  100 %  execution(void com.jmdoudoux.test.spring.service.
PersonneServiceImpl.ajouter(Personne))

2011-08-09 18:57:51,187  INFO [com.jmdoudoux.test.spring.aspect.TraceInvocation] 
Debut methode : void com.jmdoudoux.test.spring.service.PersonneServiceImpl.
afficher() avec les parametres : ()
2011-08-09 18:57:51,437  INFO [com.jmdoudoux.test.spring.aspect.TraceInvocation] 
Fin methode :  public void com.jmdoudoux.test.spring.service.
PersonneServiceImpl.afficher() retour=null
2011-08-09 18:57:51,437  INFO [com.jmdoudoux.test.spring.aspect.MonitorePerf] 
temps d'execution : StopWatch 'com.jmdoudoux.test.spring.aspect.MonitorePerf': 
running time (millis) = 250
-----------------------------------------
ms     %     Task name
-----------------------------------------
00250  100 %  execution(void com.jmdoudoux.test.spring.service.
PersonneServiceImpl.afficher())

2011-08-09 18:57:51,437  INFO [com.jmdoudoux.test.spring.MonApp] Fin invocation 
du service
2011-08-09 18:57:51,437  INFO [com.jmdoudoux.test.spring.MonApp] Fin de 
l'application

L'avantage de cette solution est qu'elle permet d'ajouter des aspects sur des beans qui ne sont pas gérés par Spring.

 

85.3. Spring AOP et AspectJ

Il est possible dans une même application d'utiliser des aspects tissés par Spring AOP et par AspectJ avec LTW.

Lorsque les aspects sont définis en utilisant les annotations d'AspectJ, il est nécessaire de préciser les aspects qui seront tissés par Spring AOP et ceux qui le seront par AspectJ.

Les aspects pris en charge par Spring AOP doivent être précisés en utilisant le tag <aop:include> dans le fichier de configuration du contexte de Spring.

Les aspects pris en charge par AspectJ doivent être précisés dans le fichier aop.xml pour un tissage dynamique.

Ainsi chaque tisseur prendra en charge les aspects qui le concernent.

L'exemple ci-dessous va mettre en oeuvre un aspect avec Spring AOP et un autre avec AspectJ.

Exemple :
@Component("traceInvocation")
@Aspect
public class TraceInvocation {
  private static Logger LOGGER = Logger.getLogger(TraceInvocation.class);
 
  @Around("traceInvocationPointcut()")
  public Object afficherTrace(final ProceedingJoinPoint joinpoint)
    throws Throwable {
    String nomMethode = joinpoint.getTarget().getClass().getSimpleName() + "."
        + joinpoint.getSignature().getName();
    final Object[] args = joinpoint.getArgs();
    final StringBuffer sb = new StringBuffer();
   
    sb.append(joinpoint.getSignature().toString());
    sb.append(" avec les parametres : (");
    for (int i = 0; i < args.length; i++) {
      sb.append(args[i]);
      if (i < args.length - 1) {
        sb.append(", ");
      }
    }
   
    sb.append(")");
   
    LOGGER.info("Debut methode : " + sb);
    Object obj = joinpoint.proceed();
   
    LOGGER.info("Fin methode : " + nomMethode + " retour=" + obj);
    return obj;
  }
 
  @Pointcut("execution(* com.jmdoudoux.test.spring.service.*ServiceImpl.*(..))")
  public void traceInvocationPointcut() {
  }
}

Cet aspect va être pris en charge par Spring AOP. Dans le fichier de déclaration du contexte, le bean qui encapsule l'aspect est fourni comme valeur de l'attribut name du tag <aop:include>. Ce tag est un tag fils du tag <aop:aspectj-autoproxy>

Exemple :
<?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:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop 
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/context 
http://www.springframework.org/schema/context/spring-context.xsd">
 
  <context:component-scan base-package="com.jmdoudoux.test.spring" />
 
  <aop:aspectj-autoproxy> 
    <aop:include name="traceInvocation" />
  </aop:aspectj-autoproxy>
  
</beans>

Le second aspect est écrit de manière similaire en incluant en plus une gestion de son ordre d'exécution par rapport aux autres aspects.

Exemple :
package com.jmdoudoux.test.spring.aspect;
      
import org.apache.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;

@Component
@Aspect
public class MonitorePerf implements Ordered {
  private static Logger LOGGER = Logger.getLogger(MonitorePerf.class);
  private int order;
 
  @Around("monitorePerfPointcut()")
  public Object executer(final ProceedingJoinPoint joinpoint) throws Throwable {
    Object returnValue;
   
    StopWatch clock = new StopWatch(getClass().getName());
    try {
      clock.start(joinpoint.toString());
      returnValue = joinpoint.proceed();
    } finally {
      clock.stop();
      LOGGER.info("temps d'execution : " + clock.prettyPrint());
    }
    return returnValue;
  }
 
  @Override
  public int getOrder() {
    return order;
  }
 
  @Pointcut("execution(* com.jmdoudoux.test.spring.service.*ServiceImpl.*(..))")
  public void monitorePerfPointcut() {
  }
 
  @Value("2")
  public void setOrder(final int order) {
    this.order = order;
  }
}

Cet aspect est pris en charge par AspectJ grâce à une configuration définie dans un fichier nommé aop-ajc.xml stocké dans le sous-répertoire META-INF.

Exemple :
<?xml version="1.0" encoding="UTF-8"?>
<aspectj>
  <weaver options="-XnoInline -Xlint:ignore -verbose -showWeaveInfo">   
    <include name="com.jmdoudoux.test.spring.service.*ServiceImpl"/> 
  </weaver>
 
  <aspects> 
    <aspect name="com.jmdoudoux.test.spring.aspect.MonitorePerf"/>
  </aspects>
</aspectj>

Ce fichier de configuration d'AspectJ permet de préciser :

  • les options utilisées par le tisseur d'AspectJ en utilisant l'attribut options du tag <weaver>
  • au tisseur sur quelles classes il doit réaliser son action en utilisant le tag <include> fils du tag <weaver> : dans l'exemple ci-dessus, c'est toutes les classes dont le nom se termine par ServiceImpl du package com.jmdoudoux.test.spring.service.
  • quels aspects doivent être tissés en utilisant le tag <aspect> du tag <aspects> : dans l'exemple ci-dessus, c'est uniquement l'aspect encapsulé dans la classe MonitorePerf.

Les autres fichiers sont identiques à ceux des exemples précédents.

Pour permettre le tissage au runtime par AspectJ, il est nécessaire de préciser l'agent adéquat à la JVM en utilisant l'option -javaagent:chemin_vers_aspectjweaver.jar

Exemple :

-javaagent:lib/aspectjweaver-1.6.1.jar

Le classpath de l'application contient les bibliothèques : org.springframework.aop-3.0.5.RELEASE.jar, org.springframework.asm-3.0.5.RELEASE.jar, org.springframework.aspects-3.0.5.RELEASE.jar, org.springframework.beans-3.0.5.RELEASE.jar, org.springframework.context-3.0.5.RELEASE.jar, org.springframework.core-3.0.5.RELEASE.jar, org.springframework.expression-3.0.5.RELEASE.jar, org.springframework.instrument-3.0.5.RELEASE.jar, org.apache.commons.logging-1.1.1.jar, aspectrt.jar, log4j-1.2.16.jar, aopalliance-1.0.0.jar, com.springframework.org.aspectj.weaver.-.1.6.8.RELEASE.jar,

Résultat :
[AppClassLoader@fabe9] info AspectJ Weaver Version 1.6.8 
built on Friday Jan 8, 2010 at 21:53:37 GMT
[AppClassLoader@fabe9] info register classloader sun.misc.Launcher$AppClassLoader@fabe9
[AppClassLoader@fabe9] info using configuration
file:/C:/java/api/spring-framework-3.0.5.RELEASE/dist/
org.springframework.aspects-3.0.5.RELEASE.jar!/META-INF/aop.xml
[AppClassLoader@fabe9] info using configuration
/C:/Documents%20and%20Settings/
jm/Documents/workspace-sts-2.5.1.RELEASE/TestSpringAOPetAspectJ/bin/META-INF/aop-ajc.xml
[AppClassLoader@fabe9] info register aspect org.springframework.beans.factory.
aspectj.AnnotationBeanConfigurerAspect
[AppClassLoader@fabe9] info register aspect org.springframework.scheduling.
aspectj.AnnotationAsyncExecutionAspect
[AppClassLoader@fabe9] info register aspect org.springframework.transaction.
aspectj.AnnotationTransactionAspect
[AppClassLoader@fabe9] info register aspect com.jmdoudoux.test.spring.aspect.
MonitorePerf
2011-07-04 19:25:13,140  INFO [com.jmdoudoux.test.spring.MonApp] Debut de l'application
2011-07-04 19:25:13,406  INFO [org.springframework.context.support. 
ClassPathXmlApplicationContext] Refreshing  org.springframework.context.support.
ClassPathXmlApplicationContext@1860038: startup date [Tue Sep 06 19:25:13 CEST 2011]; 
root of context hierarchy
2011-07-04 19:25:13,687  INFO [org.springframework.beans.factory.xml.
 XmlBeanDefinitionReader] Loading XML bean
definitions from class path resource [appContext.xml]
[AppClassLoader@fabe9] weaveinfo Join point 'method-execution(void com.
jmdoudoux.test.spring.service.PersonneServiceImpl.afficher())'in Type 'com.
jmdoudoux.test.spring.service.PersonneServiceImpl'
(PersonneServiceImpl.java:17) 
advised by around advice from 'com.jmdoudoux.test.spring.aspect.MonitorePerf'
 (MonitorePerf.java)
[AppClassLoader@fabe9] weaveinfo Join point 'method-execution(void com.
jmdoudoux.test.spring.service.PersonneServiceImpl.ajouter(com.jmdoudoux.test.
spring.entite.Personne))' in Type 'com.jmdoudoux.test.spring.service.
 PersonneServiceImpl'(PersonneServiceImpl.java:27) advised by around advice from 
'com.jmdoudoux.test.spring.aspect.MonitorePerf' (MonitorePerf.java)
2011-07-04 19:25:14,750  INFO [org.springframework.beans.factory.support.
DefaultListableBeanFactory] Pre-instantiating singletons in org.springframework.
beans.factory.support.DefaultListableBeanFactory@79801c:
defining beans [
monitorePerf,traceInvocation,personne1,personne2,personneService,org.
springframework.context.annotation.internalConfigurationAnnotationProcessor,org.
springframework.context.annotation.internalAutowiredAnnotationProcessor,org.
springframework.context.annotation.internalRequiredAnnotationProcessor,org.
springframework.context.annotation.internalCommonAnnotationProcessor,org.
springframework.aop.config.internalAutoProxyCreator];
root of factory hierarchy
Invocation constructeur PersonneServiceImpl()
2011-07-04 19:25:15,015  INFO [com.jmdoudoux.test.spring.MonApp] Debut invocation du service
2011-07-04 19:25:15,046  INFO [com.jmdoudoux.test.spring.aspect.TraceInvocation] 
Debut methode : void com.jmdoudoux.test.spring.service.PersonneService.ajouter(
Personne) avec les parametres : (com.jmdoudoux.test.spring.entite.Personne@6e96ff)
2011-07-04 19:25:15,546  INFO [com.jmdoudoux.test.spring.aspect.MonitorePerf] 
temps d'execution : StopWatch 'com.jmdoudoux.test.spring.aspect.MonitorePerf': 
running time (millis) = 500
-----------------------------------------
ms    
%     Task name
-----------------------------------------
00500  100
%  execution(void com.jmdoudoux.test.spring.service.PersonneServiceImpl.ajouter(Personne))
2011-07-04 19:25:15,546  INFO [com.jmdoudoux.test.spring.aspect.TraceInvocation] 
Fin methode : 
PersonneServiceImpl.ajouter retour=null
2011-07-04 19:25:15,546  INFO [com.jmdoudoux.test.spring.aspect.TraceInvocation] 
Debut methode : void com.jmdoudoux.test.spring.service.PersonneService.afficher(
) avec les parametres : ()
2011-07-04 19:25:15,796  INFO [com.jmdoudoux.test.spring.aspect.MonitorePerf] 
temps d'execution : StopWatch 'com.jmdoudoux.test.spring.aspect.MonitorePerf': 
running time (millis) = 250
-----------------------------------------
ms    
%     Task name
-----------------------------------------
00250  100
%  execution(void com.jmdoudoux.test.spring.service.PersonneServiceImpl.afficher())
2011-07-04 19:25:15,796  INFO [com.jmdoudoux.test.spring.aspect.TraceInvocation] 
Fin methode : 
PersonneServiceImpl.afficher retour=null
2011-07-04 19:25:15,796  INFO [com.jmdoudoux.test.spring.MonApp] Fin invocation du service
2011-07-04 19:25:15,796  INFO [com.jmdoudoux.test.spring.MonApp] Fin de l'application

 

85.4. L'utilisation des namespaces

 

85.4.1. L'utilisation du tag <context:load-time-weaver>

Depuis la version 2.5 de Spring, il est possible d'utiliser le tag <context:load-time-weaver> dans le fichier de configuration pour demander le tissage en utilisant un agent fourni par Spring plutôt que l'agent d'AspectJ.

Attention : le tissage par l'agent Spring ne peut se faire que sur des objets qui sont définis dans le contexte et donc gérés dans le conteneur.

La bibliothèque spring-agent doit être ajoutée au classpath.

La JVM doit être lancée avec l'option -javaagent:chemin bibliothèque spring-agent.jar

Si le tag <context:load-time-weaver> est utilisé dans une application web déployée dans un conteneur web Tomcat, il faut préciser l'utilisation d'un classloader dédié dans le fichier META-INF/context.xml de la webapp

Exemple :
<?xml version='1.0' encoding='utf-8'?>
<Context reloadable="true">
  <Loader loaderClass=
    "org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader"/>
</Context>

 


Développons en Java v 2.20   Copyright (C) 1999-2021 Jean-Michel DOUDOUX.   
[ Précédent ] [ Sommaire ] [ Suivant ] [ Télécharger ]      [ Accueil ] [ Commentez ]