Annotated Control Flow

Overview

The principles behind CFlow when using annotations is exactly the same as when using XML, all that is different is the way of defining it.

@CFlowStackDef and @CFlowDef

MyAspect contains a few @CFlowStackDef annotated fields:

   @CFlowStackDef (cflows={@CFlowDef(expr = "void POJO->method1()", called=true), @CFlowDef(expr = "void POJO->method2()", called=true), @CFlowDef(expr = "void POJO->method3()", called=true)})
   public static CFlowStack cf123Before;

The first one declares that method1, method2, and method3 should be in the call stack in that order. The next

   @CFlowStackDef (cflows={@CFlowDef(expr = "POJO->new()", called=true), @CFlowDef(expr = "void POJO->method3()", called=true)})
   public static CFlowStack cf1ConAnd3;
Says that the POJO constructor with an int parameter and method3 must be in the call stack in that order. As for Annotated composition and Annotated typedefs the fully qualified name of the @CFlowStackDef annotated field is used when we reference the CFlowStackDef by name. Again, the type CFlowStack is used as the type of the field for clarity, even though the actual type of the field is irrelevant.

Apply a @CFlowStackDef

You can reference the CFlowStackDef from within a @Bind annotation, by using the name of the field that was annotated. They can be compositional as well. The following binding in MyAspect uses the defined CFlowStackDefs

   @Bind (pointcut="execution(void POJO->method4())", cflow="(MyAspect.cf123Before OR MyAspect.cf123Before)")
   public Object methodAdvice(MethodInvocation invocation) throws Throwable
   {
      ...
   }

This says to trigger the SimpleInterceptor on the execution of method4, but only when it is called within the context of method1, method2, and method3 OR contructor and method3.

The next example in MyAspect is for recursive methods. The example CFlowStackDef says that two calls to the recursive method must be in the call stack, but no more

   @CFlowStackDef (cflows={@CFlowDef(expr = "void POJO->recursive(int)", called=true), @CFlowDef(expr = "void POJO->recursive(int)", called=true), @CFlowDef(expr = "void POJO->recursive(int)", called=false)})
   public static CFlowStack cf2Recursions;

   @Bind (pointcut="execution(void POJO->recursive(int))", cflow="MyAspect.cf2Recursions")
   public Object recursiveAdvice(MethodInvocation invocation) throws Throwable
   {
      ...
   }

Combined with the execution binding, the SimpleInterceptor will only be triggered on the second call to the recursive method.

Run the example

To compile and run:
  $ ant
It will javac the files and then run the AOPC precompiler to manipulate the bytecode, then finally run the example. The output should read as follows:
run:
     [java] --- pojo.method4(); ---
     [java] method4
     [java] --- pojo.method3(); ---
     [java] method3
     [java] method4
     [java] --- pojo.method2(); ---
     [java] method2
     [java] method3
     [java] method4
     [java] --- pojo.method1(); ---
     [java] method1
     [java] method2
     [java] method3
     [java] <<< MyAdvice.methodAdvice accessing: public void POJO.method4()
     [java] method4
     [java] >>> Leaving MyAdvice.methodAdvice
     [java] --- pojo.recursive(); ---
     [java] recursive: 1
     [java] recursive: 2
     [java] recursive: 3
     [java] --- new POJO(int); ---
     [java] method3
     [java] method4