Introduction to Aspect-Oriented Programming Martin Giese Chalmers University of Technology Göteborg, Sweden AOP Course 2003 – p.1/44 AspectJ Quick Tour AOP Course 2003 – p.2/44 Reminder: Join Points A join point is a well-defined point in the execution of a program. Examples: • a method/constructor call • execution of a method/constructor implementation • read/write access to a field • .. . Program flow can enter and leave a join point. AOP Course 2003 – p.3/44 Reminder: Pointcuts A pointcut is the static designation of some set of join points for every execution of the program. Examples: • all calls to public methods of the Point class • every execution of a constructor with one int argument • every write access to a public field • .. . AOP Course 2003 – p.4/44 Code Example: Figure Editor Figure * +makePoint(..) +makeLine(..) FigureElement +moveBy(int,int) Display +update() Point +getX() +getY() +setX(int) +setY(int) +moveBy(int,int) Line 2 +getP1() +getP2() +setP1(Point) +setP2(Point) +moveBy(int,int) AOP Course 2003 – p.5/44 Code Example (cont.) class Line implements FigureElement { private Point p1, p2; Point getP1() { return p1; } Point getP2() { return p2; } void setP1(Point p1) { this.p1 = p1; } void setP2(Point p2) { this.p2 = p2; } void moveBy(int dx, int dy) { p1.moveBy(dx,dy); p2.moveBy(dx,dy); } } class Point implements FigureElement { private int x = 0, y = 0; int getX() { return x; } int getY() { return y; } void setX(int x) { this.x = x; } void setY(int y) { this.y = y; } void moveBy(int dx, int dy) { x += dx; y += dy; } } AOP Course 2003 – p.6/44 Pointcuts in AspectJ Primitive pointcuts: call(void Point.setX(int)) each join point that is a call to a method that has the signature void Point.setX(int) Also for interface signatures: call(void FigureElement.moveBy(int,int)) Each call to the moveBy(int,int) method in a class that implements FigureElement More later. . . AOP Course 2003 – p.7/44 Composition of Pointcuts Pointcuts can be joined using boolean operators &&,||,!. call(void Point.setX(int)) || call(void Point.setY(int)) calls to the setX and setY methods of Point. Question: what does this select? call(void Line.setP1(Point)) && call(void Line.setP2(Point)) AOP Course 2003 – p.8/44 Join points from many types Join points from different types possible: call(void FigureElement.moveBy(int,int)) || call(void Point.setX(int)) || call(void Point.setY(int)) || call(void Line.setP1(Point)) || call(void Line.setP2(Point)) Any call to a state-changing method in the given FigureElement classes AOP Course 2003 – p.9/44 Named Pointcuts Pointcuts can be declared to give them a name: pointcut stateChange() : call(void FigureElement.moveBy(int,int)) || call(void Point.setX(int)) || call(void Point.setY(int)) || call(void Line.setP1(Point)) || call(void Line.setP2(Point)); ➠ Analogous to method declaration or typedef in C. After declaration, stateChange() can be used wherever a pointcut is expected. AOP Course 2003 – p.10/44 Wildcards Method signatures can contain wildcards: call(void java.io.PrintStream.println(*)) any PrintStream method named println returning void and taking exactly one argument of any type. call(public * Figure.*(..)) any public method in Figure. call(public * Line.set*(..)) any method in Line with a name starting with set. AOP Course 2003 – p.11/44 Example The pointcut from before, using wildcards: pointcut stateChange() : call(void FigureElement.moveBy(int,int)) || call(* Point.set*(*)) || call(* Line.set*(*)); AOP Course 2003 – p.12/44 Advice in AspectJ Advice can be attached to join points: before(): stateChange() { System.out.println("about to change state"); } after() returning: stateChange() { System.out.println("just successfully changed state"); } AOP Course 2003 – p.13/44 Aspects in AspectJ public aspect DisplayUpdating { pointcut stateChange() : call(void FigureElement.moveBy(int,int)) || call(* Point.set*(*)) || call(* Line.set*(*)); after() returning : stateChange() { Display.update(); } } ➠ After every state changing call, update the display. AOP Course 2003 – p.14/44 Compilation No partial compilation in AspectJ! ➠ Always provide all class and aspect source files. ajc Main.java Display.java Figure.java FigureElement.java \ Line.java Point.java DisplayUpdating.java or ajc -argfile files.lst run using: java Main AOP Course 2003 – p.15/44 AspectJ Details AOP Course 2003 – p.16/44 Type Patterns Used to denote a set of types. • int • Line • * • java.util.Map* • java..*Factory* • java.io.OutputStream+ • Foo+ && !Foo • Foo+ && !(java..Bar*[]) AOP Course 2003 – p.17/44 Method Patterns Used to denote a set of methods in one or more classes. Method declaration: public final void write(int x) throws IOException Method pattern: public final void Writer.write(int) throws IOException Wildcards possible everywhere. . . • void *.write(int) • void Foo+.set*(int) AOP Course 2003 – p.18/44 Method Patterns (cont.) • * Foo.write(*) • * Foo.write(..) • * Foo.write(..,int) • * *.*(..) • public Foo.*(..) • !public *.*(..) • * *.*(..) throws java.io.IOException • * *.*(..) throws !java.io.IOException • * *.*(..) throws (!java.io.IOException) AOP Course 2003 – p.19/44 Constructor and Field Patterns Select a set of constructors: Like method patterns, with name new and no return type: • Line.new(int) • public *.new(..) • .. . Field Patterns: • int Line.x • public !final * FigureElement+.* • .. . AOP Course 2003 – p.20/44 Primitive Pointcuts • call(MethodPattern) • call(ConstructorPattern) • execution(MethodPattern) • execution(ConstructorPattern) • get(FieldPattern) • set(FieldPattern) • handler(TypePattern) AOP Course 2003 – p.21/44 Restricting the Scope Static Scope: • within(TypePattern) • withincode(MethodPattern) • withincode(ConstructorPattern) Dynamic Scope: • cflow(Pointcut) • cflowbelow(Pointcut) AOP Course 2003 – p.22/44 Examples Match constructor calls from outside factory: call(FigureElement+.new()) && !within(Figure) Match state changing calls that are not due to other state changing calls: stateChange() && !cflowbelow(stateChange()) Match state changes that are not due to some method in Figure: stateChange() && !cflow(execution(* Figure.*(..))) AOP Course 2003 – p.23/44 Example: cflow caller point 1 moveBy a line moveBy point 2 moveBy AOP Course 2003 – p.24/44 Pointcuts with Parameters One often needs information about the context of a join point. ➠ use pointcuts with parameters. Example: pointcut stateChange(FigureElement figElt) : target(figElt) && ( call(void FigureElement.moveBy(int,int)) || call(* Point.set*(*)) || call(* Line.set*(*)) ); after(FigureElement fe) : stateChange(fe) {...} AOP Course 2003 – p.25/44 Parameters in pointcut declaration pointcut stateChange(FigureElement figElt) : target(figElt) && ( call(void FigureElement.moveBy(int,int)) || call(* Point.set*(*)) || call(* Line.set*(*)) ); • figElt declared in ‘header’, together with name. • bound by the target pointcut target alone matches any non-static call, field access, etc. if the target type matches the declared parameter type. AOP Course 2003 – p.26/44 Parameters in advice declaration after(FigureElement fe) : stateChange(fe) { . . . code can use fe. . . } after(FigureElement fe) : target(fe) && ... { . . . code can use fe. . . } • fe declared in ‘header’ • bound by the pointcut ➠ values move from right to left over the colon. AOP Course 2003 – p.27/44 Some more Pointcuts • target(Id) • target(Type) • this(Type or Id) • args(Type or Id. . . ) • if(boolean expression) AOP Course 2003 – p.28/44 Example: Diagonal Moves Define a pointcut for moves with equal dx and dy. pointcut diagMove() : call(void FigureElement.moveBy(int,int)) && args(dx,dx); AOP Course 2003 – p.29/44 Example: Diagonal Moves Define a pointcut for moves with equal dx and dy. pointcut diagMove(int dx) : call(void FigureElement.moveBy(int,int)) && args(dx,dx); AOP Course 2003 – p.30/44 Example: Diagonal Moves Define a pointcut for moves with equal dx and dy. pointcut diagMove(int dx,int dy) : call(void FigureElement.moveBy(int,int)) && args(dx,dy) && if(dx==dy); AOP Course 2003 – p.31/44 Example: Diagonal Moves Define a pointcut for moves with equal dx and dy. pointcut diagHelp(int dx,int dy) : call(void FigureElement.moveBy(int,int)) && args(dx,dy) && if(dx==dy); pointcut diagMove(int dxy) : diagHelp(dxy,int); AOP Course 2003 – p.32/44 About Conditionals AspectJ specification: The boolean expression used can only access static members, variables exposed by the enclosing pointcut or advice, (and thisJoinPoint forms). But still. . . static methods may be called, which may have side effects! ➠ left to right evaluation order of pointcuts. Exercise: What about join points of static functions called in if? AOP Course 2003 – p.33/44 Advice in AspectJ ‘Before’ advice: before(formal parameters) : Pointcut { . . . advice body. . . } The advice body gets executed every time just before the program flow enters a join point matched by the pointcut. The formal parameters receive values from the pointcut. AOP Course 2003 – p.34/44 After Advice ‘After’ advice: after(formal parameters) returning : Pointcut {...} The advice body gets executed every time just after the program flow exits a join point matched by the pointcut by returning normally. Capture return value: after(...) returning (int ret): Pointcut {...} AOP Course 2003 – p.35/44 After Advice (cont.) Advice after throwing an exception: after(formal parameters) throwing : Pointcut {...} Capture thrown exception: after(...) throwing (Exception e): Pointcut {...} Match normal and abrupt return: after(...) : Pointcut {...} AOP Course 2003 – p.36/44 Around Advice Run advice instead of original code: Type around(. . . ) : . . . { ... proceed(. . . ); ... } • run advice body instead of original call, field access, method body, etc. • use proceed to use the original join point, if needed. AOP Course 2003 – p.37/44 Example: Caching Cache values from an expensive function call. int around(int x) : call(int Expensive.f(int)) && args(x) { if (cache.contains(x)) { return cache.get(x); } else { int result = proceed(x); cache.put(x,result); return result; } } AOP Course 2003 – p.38/44 thisJoinPoint For tracing or logging join points, matched methods might have very different signatures. ➠ difficult to pass all useful information through parameters. Special variable thisJoinPoint available in advice. • thisJoinPoint.toString() • thisJoinPoint.getTarget() • thisJoinPoint.getArgs() thisJoinPoint object expensive to construct. thisJoinPointStaticPart is statically constructed. AOP Course 2003 – p.39/44 Aspects in AspectJ Similar to class declarations, but • Can also contain pointcut and advice declarations, • System and not user controls instantiation, (An aspect gets instantiated the first time, some advice in it needs to be executed.) • Aspects can extend only abstract aspects, (They can extend classes and implement interfaces) • Classes cannot extend aspects. AOP Course 2003 – p.40/44 Caching Aspect package se.chalmers.cs.caching; import se.chalmers.cs.calculations.Expensive; public aspect CacheExpensiveFunction { private IntegerCache cache = new IntegerCache(); int around(int x) : call(int Expensive.f(int)) && args(x) { if (cache.contains(x)) { return cache.get(x); } else { int result = proceed(x); cache.put(x,result); return result; } } } AOP Course 2003 – p.41/44 Tracing Aspect (first try) package tracing; public aspect TraceAllCalls { pointcut pointsToTrace() : call(* *.*(..)) ; before() : pointsToTrace() { System.err.println("Enter " + thisJoinPoint); } after() : pointsToTrace() { System.err.println("Exit " + thisJoinPoint); } } AOP Course 2003 – p.42/44 Tracing Aspect package tracing; public aspect TraceAllCalls { pointcut pointsToTrace() : call(* *.*(..)) && !within(TraceAllCalls); before() : pointsToTrace() { System.err.println("Enter " + thisJoinPoint); } after() : pointsToTrace() { System.err.println("Exit " + thisJoinPoint); } } AOP Course 2003 – p.43/44 Conclusion You should now know about. . . • AspectJ’s main kinds of primitive pointcuts • named pointcuts • pointcuts with parameters • different kinds of advice • thisJoinPoint • syntax for aspects Tomorrow, you will learn about inter-type declarations and other advanced stuff. AOP Course 2003 – p.44/44