Ceci est une ancienne révision du document !
Table des matières
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 latarget
.
Join Point | this | target | Arguments |
---|---|---|---|
Method Call | executing object* | target object** | method arguments |
Method Execution | executing object* | executing object* | method arguments |
Constructor Call | executing object* | None | constructor arguments |
Constructor Execution | executing object | executing object | constructor arguments |
Static initializer execution | None | None | None |
Object pre-initialization | None | None | constructor arguments |
Object initialization | executing object | executing object | constructor arguments |
Field reference | executing object* | target object** | None |
Field assignment | executing object* | target object** | assigned value |
Handler execution | executing object* | executing object* | caught exception |
Advice execution | executing aspect | executing aspect | advice 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'utiliserexecution
.
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.
(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 aussibefore
ouaround
:
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
.
<note warning>Il est interdit de mélanger l'écriture de code avec annotation et sans annotation (cf Source, Archive). Il n'est donc pas possible d'utiliser la moindre annotation dans une classe nécessitant d'avoir des droits privilégiés.</note>
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
.
Exemple avec before
Code source
- 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 chaque 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 l'intérieur de 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(); } } }
Exemple avec around
Code source
- C.java
public class C { public int i = 0; void incI(int x) { System.out.println("incI" + i); i = i + x; } void incI() { System.out.println("incI" + i); i = i + 1; } static public void main(String[] arg) { C c = new C(); c.incI(50); c.incI(500); c.incI(1500); } }
- A.aj
public privileged aspect A { private static final int MAX = 1000; void around(C c, int x): call(void C.incI(int)) && target(c) && args(x) { if (c.i + x > MAX) throw new RuntimeException(); proceed(c, x); } }
- A2.aj
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 A2 { private static final int MAX = 1000; @Pointcut("call(void C.incI(int)) && args(x)") private void CoupureIncI(int x) { } @Around("CoupureIncI(x)") public void AutourDeCoupureIncI(ProceedingJoinPoint joinPoint, int x) throws Throwable { if (((C) joinPoint.getTarget()).i + x > MAX) throw new RuntimeException(); joinPoint.proceed(new Object[] { x, joinPoint.getTarget() }); } }
Version décompilée avec l'advice A seulement
- C.java
import java.io.PrintStream; import org.aspectj.runtime.internal.AroundClosure; public class C { public int i = 0; void incI(int x) { System.out.println("incI" + this.i); this.i += x; } private static final void incI_aroundBody1$advice(C target, int x, A ajc$aspectInstance, C c, int x, AroundClosure ajc$aroundClosure) { if (c.i + x > 1000) { throw new RuntimeException(); } AroundClosure localAroundClosure = ajc$aroundClosure;int j = x;C localC = c;incI_aroundBody0(localC, j); } private static final void incI_aroundBody3$advice(C target, int x, A ajc$aspectInstance, C c, int x, AroundClosure ajc$aroundClosure) { if (c.i + x > 1000) { throw new RuntimeException(); } AroundClosure localAroundClosure = ajc$aroundClosure;int j = x;C localC = c;incI_aroundBody2(localC, j); } private static final void incI_aroundBody5$advice(C target, int x, A ajc$aspectInstance, C c, int x, AroundClosure ajc$aroundClosure) { if (c.i + x > 1000) { throw new RuntimeException(); } AroundClosure localAroundClosure = ajc$aroundClosure;int j = x;C localC = c;incI_aroundBody4(localC, j); } void incI() { System.out.println("incI" + this.i); this.i += 1; } private static final void incI_aroundBody0(C paramC, int paramInt) { paramC.incI(paramInt); } private static final void incI_aroundBody2(C paramC, int paramInt) { paramC.incI(paramInt); } public static void main(String[] arg) { C c = new C(); int j = 50;C localC1 = c;incI_aroundBody1$advice(localC1, j, A.aspectOf(), localC1, j, null); int k = 500;C localC2 = c;incI_aroundBody3$advice(localC2, k, A.aspectOf(), localC2, k, null); int m = 1500;C localC3 = c;incI_aroundBody5$advice(localC3, m, A.aspectOf(), localC3, m, null); } private static final void incI_aroundBody4(C paramC, int paramInt) { paramC.incI(paramInt); } }
- A.aj
import org.aspectj.internal.lang.annotation.ajcPrivileged; import org.aspectj.lang.NoAspectBoundException; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.runtime.internal.AroundClosure; @Aspect @ajcPrivileged public class A { private 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; } } @Around(value="(call(void C.incI(int)) && (target(c) && args(x)))", argNames="c,x,ajc$aroundClosure") public void ajc$around$test_A$1$ff7f72c0(C c, int x, AroundClosure ajc$aroundClosure) { if (c.i + x > 1000) { throw new RuntimeException(); } ajc$around$test_A$1$ff7f72c0proceed(c, x, ajc$aroundClosure); } }
Version décompilée avec l'advice A2 seulement
- C.java
import java.io.PrintStream; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.JoinPoint.StaticPart; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.runtime.internal.Conversions; import org.aspectj.runtime.reflect.Factory; public class C { private static void ajc$preClinit() { Factory localFactory = new Factory("C.java", C.class);ajc$tjp_0 = localFactory.makeSJP("method-call", localFactory.makeMethodSig("0", "incI", "test.C", "int", "x", "", "void"), 18);ajc$tjp_1 = localFactory.makeSJP("method-call", localFactory.makeMethodSig("0", "incI", "test.C", "int", "x", "", "void"), 19);ajc$tjp_2 = localFactory.makeSJP("method-call", localFactory.makeMethodSig("0", "incI", "test.C", "int", "x", "", "void"), 20); } public int i = 0; private static final JoinPoint.StaticPart ajc$tjp_0; private static final JoinPoint.StaticPart ajc$tjp_1; private static final JoinPoint.StaticPart ajc$tjp_2; void incI(int x) { System.out.println("incI" + this.i); this.i += x; } void incI() { System.out.println("incI" + this.i); this.i += 1; } private static final void incI_aroundBody0(C paramC, int paramInt, JoinPoint paramJoinPoint) { paramC.incI(paramInt); } private static final void incI_aroundBody2(C paramC, int paramInt, JoinPoint paramJoinPoint) { paramC.incI(paramInt); } public static void main(String[] arg) { C c = new C(); int j = 50;C localC1 = c;JoinPoint localJoinPoint1 = Factory.makeJP(ajc$tjp_0, null, localC1, Conversions.intObject(j));incI_aroundBody1$advice(localC1, j, localJoinPoint1, A2.aspectOf(), (ProceedingJoinPoint)localJoinPoint1, j); int k = 500;C localC2 = c;JoinPoint localJoinPoint2 = Factory.makeJP(ajc$tjp_1, null, localC2, Conversions.intObject(k));incI_aroundBody3$advice(localC2, k, localJoinPoint2, A2.aspectOf(), (ProceedingJoinPoint)localJoinPoint2, k); int m = 1500;C localC3 = c;JoinPoint localJoinPoint3 = Factory.makeJP(ajc$tjp_2, null, localC3, Conversions.intObject(m));incI_aroundBody5$advice(localC3, m, localJoinPoint3, A2.aspectOf(), (ProceedingJoinPoint)localJoinPoint3, m); } private static final void incI_aroundBody4(C paramC, int paramInt, JoinPoint paramJoinPoint) { paramC.incI(paramInt); } private static final void incI_aroundBody1$advice(C target, int x, JoinPoint thisJoinPoint, A2 ajc$aspectInstance, ProceedingJoinPoint joinPoint, int x) { if (((C)joinPoint.getTarget()).i + x > 1000) { throw new RuntimeException(); } Object[] arrayOfObject = { Integer.valueOf(x), joinPoint.getTarget() };ProceedingJoinPoint localProceedingJoinPoint = joinPoint;incI_aroundBody0(target, Conversions.intValue(arrayOfObject[0]), localProceedingJoinPoint);null; } private static final void incI_aroundBody3$advice(C target, int x, JoinPoint thisJoinPoint, A2 ajc$aspectInstance, ProceedingJoinPoint joinPoint, int x) { if (((C)joinPoint.getTarget()).i + x > 1000) { throw new RuntimeException(); } Object[] arrayOfObject = { Integer.valueOf(x), joinPoint.getTarget() };ProceedingJoinPoint localProceedingJoinPoint = joinPoint;incI_aroundBody2(target, Conversions.intValue(arrayOfObject[0]), localProceedingJoinPoint);null; } private static final void incI_aroundBody5$advice(C target, int x, JoinPoint thisJoinPoint, A2 ajc$aspectInstance, ProceedingJoinPoint joinPoint, int x) { if (((C)joinPoint.getTarget()).i + x > 1000) { throw new RuntimeException(); } Object[] arrayOfObject = { Integer.valueOf(x), joinPoint.getTarget() };ProceedingJoinPoint localProceedingJoinPoint = joinPoint;incI_aroundBody4(target, Conversions.intValue(arrayOfObject[0]), localProceedingJoinPoint);null; } static {} }
- A2.aj
import org.aspectj.lang.NoAspectBoundException; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; @Aspect public class A2 { private static final int MAX = 1000; private static Throwable ajc$initFailureCause; public static final A2 ajc$perSingletonInstance; static { try { ajc$postClinit(); } catch (Throwable localThrowable) { ajc$initFailureCause = localThrowable; } } private static void ajc$postClinit() { ajc$perSingletonInstance = new A2(); } public static boolean hasAspect() { return ajc$perSingletonInstance != null; } public static A2 aspectOf() { if (ajc$perSingletonInstance == null) { throw new NoAspectBoundException("test.A2", ajc$initFailureCause); } return ajc$perSingletonInstance; } @Around("CoupureIncI(x)") public void AutourDeCoupureIncI(ProceedingJoinPoint joinPoint, int x) throws Throwable { if (((C)joinPoint.getTarget()).i + x > 1000) { throw new RuntimeException(); } joinPoint.proceed(new Object[] { Integer.valueOf(x), joinPoint.getTarget() }); } }
<note>Il n'y a finalement pas de différence entre la méthode avec annotation et sans annotation, hormis l'utilisation de ProceedingJoinPoint.</note>
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 …; } }