Outils pour utilisateurs

Outils du site


lang:java:aspectj

Ceci est une ancienne révision du document !


Manuel complet, Archive

Complément Java 5, Archive

Déclaration d'un aspect

public aspect Log {
}

Déclaration d'un point de coupe

pointcut evaluation(EventHandler h): target(h) &&
  call(public void *.handleEvent());
  • evaluation : nom du point de coupe.
  • (EventHandler h) : liste des paramètres qui sont saisis et qui seront disponibles par les méthodes utilisant la coupe. Il faut préciser après chaque paramètre qui est entre les parenthèses.
  • target(h) : h est la target.
Join PointthistargetArguments
Method Callexecuting object*target object**method arguments
Method Executionexecuting object*executing object*method arguments
Constructor Callexecuting object*Noneconstructor arguments
Constructor Executionexecuting objectexecuting objectconstructor arguments
Static initializer executionNoneNoneNone
Object pre-initializationNoneNoneconstructor arguments
Object initializationexecuting objectexecuting objectconstructor arguments
Field referenceexecuting object*target object**None
Field assignmentexecuting object*target object**assigned value
Handler executionexecuting object*executing object*caught exception
Advice executionexecuting aspectexecuting aspectadvice arguments

* There is no executing object in static contexts such as static method bodies or static initializers.

** There is no target object for join points associated with static methods or fields.

  • call : méthode à intercepter. Il est possible d'utiliser execution.

La méthode call est résolue à la compilation alors que la méthode execution est résolue à l'exécution. Pour que la méthode call marche, il faut de la classe appelante soit surveillée alors qu'avec la méthode execution, seule la classe appelée doit être surveillée.

Source, Archive

  • (public void *.handleEvent()) : la méthode à surveiller.

Pour le constructeur, il utiliser la dénomination new. Par exemple : C.new().

Déclaration d'un advice

Utilisation d'un point de coupe (advice) :

after(EventHandler h):evaluation(h) {}
  • after : s'exécute une fois la méthode terminée. Il existe aussi before ou around :
around() : call(Display.update()) { if (! Display.disabled()) proceed();}

Ici, le point de coupe est directement directement défini dans l'advice. C'est à around de bien appeler proceed(); qui permet l'exécution de la méthode Display.update().

Exemple sans annotation :

public aspect Log {
  pointcut evaluation(EventHandler h): target(h) &&
    call(public void *.handleEvent());
 
  // Le pointcut et after ont la même signature. Je ne sais pas si c'est absolument
  // indispensable mais ça a toujours été le cas dans les exemples que j'ai trouvé.
  after(EventHandler h):evaluation(h) {
    System.out.println("Coucou");
  }
}
public aspect ProceedAspect {
  pointcut setAge(int i): call(* setAge(..)) && args(i);
 
  Object around(int i): setAge(i) {
    return proceed(i*2);
  }
}

Et en version annotation Java 5 :

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
 
@Aspect
public class Log {
  @After("call(public void *.handleEvent()) && target(h)")
  public void LogHandleEvent(EventHandler h)
  {
    System.out.println("Coucou");
  }
}
@Aspect
public class ProceedAspect {
 
  @Pointcut("call(* setAge(..)) && args(i)")
  void setAge(int i) {}
 
  @Around("setAge(i)")
  public Object twiceAsOld(ProceedingJoinPoint thisJoinPoint, int i) {
    return thisJoinPoint.proceed(new Object[]{i*2}); //using Java 5 autoboxing
  }
}

Séparation du point de coupure et de l'advice, il faut passer par une méthode vide. Pas pratique.

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
 
@Aspect
public class Log {
  @Pointcut("call(public void *.handleEvent())")
  private void CoupureHandleEvent() {
  }
 
  @After("CoupureHandleEvent() && target(h)")
  public void LogHandleEvent(EventHandler h)
  {
    System.out.println("Coucou");
  }
}

Accéder aux champs privés

Mot clé : privileged. Cela n'existe pas en annotation et ne s'utilise pas en combiné avec l'annotation @Aspect.

class C {
  private int i = 0;
  void incI(int x) { i = i+x; }
}
privileged aspect A {
  static final int MAX = 1000;
  before(int x, C c): call(void C.incI(int)) && target(c) && args(x) {
    if (c.i+x > MAX) throw new RuntimeException();
  }
}

AspectJ en interne

Le compilateur ajc utilise les points de coupe et les advices pour scanner le code source et trouver toutes les méthodes appelantes et les encadre avant par un appel des méthodes before, et après par un appel des méthodes after.

Code java :

C.java
public class C {
  private int i = 0;
 
  void incI(int x) {
    i = i + x;
  }
 
  static public void main (String[] arg) {
    C c = new C();
    c.incI(50);
    c.incI(1000);
  }
}
A.aj
public privileged aspect A {
  static final int MAX = 1000;
 
  before(int x, C c): call(void C.incI(int)) && target(c) && args(x) {
    if (c.i + x > MAX)
      throw new RuntimeException();
  }
}

Version décompilée avec l'advice sur la méthode call :

C.java
public class C
{
  // Ajout de getter/setter static pour permettre d'avoir accès à la variable privée i.
  public static int ajc$get$i(C paramC)
  {
    return paramC.i;
  }
 
  public static void ajc$set$i(C paramC, int paramInt)
  {
    paramC.i = paramInt;
  }
 
  private int i = 0;
 
  void incI(int x)
  {
    this.i += x;
  }
 
  public static void main(String[] arg)
  {
    // Le code before entoure l'appel.
    C c = new C();
    int j = 50;C localC1 = c;A.aspectOf().ajc$before$test_A$1$ff7f72c0(j, localC1);localC1.incI(j);
    int k = 1000;C localC2 = c;A.aspectOf().ajc$before$test_A$1$ff7f72c0(k, localC2);localC2.incI(k);
  }
}
A.java
import org.aspectj.internal.lang.annotation.ajcPrivileged;
import org.aspectj.lang.NoAspectBoundException;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
 
@Aspect
@ajcPrivileged
public class A
{
  static final int MAX = 1000;
 
  public static A aspectOf()
  {
    if (ajc$perSingletonInstance == null) {
      throw new NoAspectBoundException("test_A", ajc$initFailureCause);
    }
    return ajc$perSingletonInstance;
  }
 
  public static boolean hasAspect()
  {
    return ajc$perSingletonInstance != null;
  }
 
  static
  {
    try
    {
 
    }
    catch (Throwable localThrowable)
    {
      ajc$initFailureCause = localThrowable;
    }
  }
 
  @Before(value="(call(void C.incI(int)) && (target(c) && args(x)))", argNames="x,c")
  public void ajc$before$test_A$1$ff7f72c0(int x, C c)
  {
    if (C.ajc$get$i(c) + x > 1000) {
      throw new RuntimeException();
    }
  }
}

Version décompilée avec l'advice sur la méthode execution :

C.java
public class C
{
  public static int ajc$get$i(C paramC)
  {
    return paramC.i;
  }
 
  public static void ajc$set$i(C paramC, int paramInt)
  {
    paramC.i = paramInt;
  }
 
  private int i = 0;
 
  void incI(int x)
  {
    // Le code before entoure la méthode surveillée
    int j = x;A.aspectOf().ajc$before$test_A$1$74272596(j, this);this.i += x;
  }
 
  public static void main(String[] arg)
  {
    C c = new C();
    c.incI(50);
    c.incI(1000);
  }
}
A.java
import org.aspectj.internal.lang.annotation.ajcPrivileged;
import org.aspectj.lang.NoAspectBoundException;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
 
@Aspect
@ajcPrivileged
public class A
{
  static final int MAX = 1000;
 
  public static A aspectOf()
  {
    if (ajc$perSingletonInstance == null) {
      throw new NoAspectBoundException("test_A", ajc$initFailureCause);
    }
    return ajc$perSingletonInstance;
  }
 
  public static boolean hasAspect()
  {
    return ajc$perSingletonInstance != null;
  }
 
  static
  {
    try
    {
 
    }
    catch (Throwable localThrowable)
    {
      ajc$initFailureCause = localThrowable;
    }
  }
 
  @Before(value="(execution(void C.incI(int)) && (target(c) && args(x)))", argNames="x,c")
  public void ajc$before$test_A$1$74272596(int x, C c)
  {
    if (C.ajc$get$i(c) + x > 1000) {
      throw new RuntimeException();
    }
  }
}

Héritage et interface

aspect A
{
  private interface HasName {}
  declare parents: (Point || Line || Square) implements HasName;
 
  private String HasName.name;
  public  String HasName.getName()  { return name; }
}

Cela indique explicitement que Point, Line et Square implémente HasName et que on ajoute un attribut name et une méthode getName accessibles uniquement par les classes aspects.

Il s'agit d'une interface aspect. Donc les méthodes qui seront dans cette interface sont également des méthodes aspect et donc uniquement accessible depuis des classes aspect.

aspect A
{
  private interface HasName
  {
    public String getNom();
  }
 
  declare parents: Point implements HasName;
 
  public String Point.getNom()
  {
    return;
  }
}
lang/java/aspectj.1481452094.txt.gz · Dernière modification : 2016/12/11 11:28 de root