Getting Started

  • Download the Spoon-AOP Jar and add it to your classpath.
  • Write a Spoon-AOP program.
  • Create the build.xml and compile with ant.

Logging Example (annotation-driven)

Here is the classical example of a Logging aspect with Spoon-AOP. Spoon-AOP makes it straightforward to write annotation-driven aspects. For this simple example, you need to create a new annotation that you will use to mark the places where the logging will occur. Note that by defining abstract enough annotations, it is most of the time possible to create aspects that will not be broken when the program is refactored. Here, we define a Log annotation:

public @interface Log {}

You can then use this annotation in your programs:

public class MyClass {
   @Log
   void m1() {...}
   @Log
   void m2() {...}
}

In order to define the aspect that actually implements the logging, you now need to extend the spoon.aop.Aspect class.

public class LoggingAspect extends Aspect {
   @Before @Log
   void doLog() { System.out.println("calling "+_targetName_); }
}

Here, we use the Spoon-AOP Before annotation to indicate that the code of the doLog method will be inserted before the target method. By default, the advice's code will be inserted at the execution side. However, it is possible to specify the side explicitly by using the side property: Before(side=Side.CALL). Since our doLog advice is annotated with the Log annotation, the target method is any method that is annotated with a Log annotation too (the annotations of the target and of the aspect element must match). Note that _targetName_, which represents the name of the target executable, is a template parameter in the Spoon sense: it is thus statically known (see the Aspect class that defines all the default accessible data). The weaver applies partial evaluation to the resulting code so that the code is simplified, as shown here:

public class MyClass {
   @Log
   void m1() {
      System.out.println("calling m1");
      ...
   }
   @Log
   void m2() {
      System.out.println("calling m2");
      ...
   }
}

Logging Example (pointcut-driven)

Even though Spoon-AOP provides natural support of annotations, it is not always suitable to use annotations, in particular when obliviousness is needed. By annotating an advice method with Any, you specify that all the methods (even annotation-free ones) will be advised. You can then write a simple Java test include or excludes methods from your advice, depending on some statically known data. For instance, to log all the methods of a class named MyClass:

public class LoggingAspect extends Aspect {
   @Before @Any
   void doLog() { 
     if(_targetClass_==MyClass.class) {
       System.out.println("calling "+_targetName_);
     }
   }
}

Note that these Java static tests are comparable to pointcuts in the classical AOP sense. If the test does not match at compile-time, no code will be inlined before the target method. The kind of tests you can write can be any Java code. Spoon will know whether the test result can be entirely calculated at compile-time or not. In the latter case (for instance when testing on a parameter value), the uncalculated code will be left untouched and a corresponding runtime residue will be inline into the woven code. Here is an example of a more complex test:

public class LoggingAspect extends Aspect {
   @Before @Any
   void doLog() { 
     if(MyClass.class.isAssignableFrom(_targetClass_) && 
        arguments[0].equals("foo")) {
       System.out.println("calling "+_targetName_);
     }
   }
}

In that case the first part of the test can be statically calculated, while the second cannot. If the target method is MyClass.m(String s), the inlined code is:

if(s.equals("foo")) {
  System.out.println("calling m");
}

In order to factorize pointcuts between several advices, you can define a normal Java method that returns a boolean. This method needs to be annotated with a Pointcut annotation in order to indicate to Spoon-AOP that the macro expansion-like mechanism should be applied in the context of the target. For example:

public class LoggingAspect extends Aspect {
   @Pointcut
   boolean log() {
     return MyClass.class.isAssignableFrom(_targetClass_) && 
            _argumentCount_>0;
   }

   @Before @Any
   void doLog() { 
     if(log() && arguments[0]=="foo") {
       System.out.println("calling "+_targetName_);
     }
   }
}