Search code examples
javaaspectj

how to pick set joinpoints in specific methods in AspectJ


I'm developing an Advice and I want to make it advise only on field sets in specific methods. I tried cflow(pointcutForSpecificMethod()) && set(* *) pointcut expression but it picks field sets in other methods under control flow of specific methods.

Any idea?


Solution

  • This is not possible directly with an exact pointcut expression, but you can use if() pointcuts to dynamically determine from the stack trace or - like in this case - from the enclosing join point static part exposed by AspectJ - what the executing method is. Here is a little example and an aspect in two variants: native syntax (my preference, more elegant and less boilerplate) and annotation-style syntax:

    package de.scrum_master.app;
    
    public class Application {
      private int field = 0;
    
      public static void main(String[] args) {
        Application app = new Application();
        app.foo(11);
        app.bar(22);
      }
    
      public void foo(int i) {
        field = i;
        bar(2 * i);
      }
    
      void bar(int i) {
        field = i;
      }
    }
    
    package de.scrum_master.aspect;
    
    import org.aspectj.lang.JoinPoint;
    
    public aspect MyAspectNative {
      pointcut pointcutForSpecificMethod() : execution(* foo(*));
    
      public static boolean executingMethodMatches(JoinPoint.StaticPart staticPart) {
        return staticPart.getSignature().toLongString().contains("de.scrum_master.app.Application.foo(int)");
      }
    
      before() :
        cflow(pointcutForSpecificMethod()) && set(* *) &&
        if(executingMethodMatches(thisEnclosingJoinPointStaticPart))
      {
        System.out.println(thisEnclosingJoinPointStaticPart);
        System.out.println(thisJoinPoint);
      }
    }
    
    package de.scrum_master.aspect;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    
    @Aspect
    public class MyAspect {
      @Pointcut("execution(* foo(*))")
      private static void pointcutForSpecificMethod() {}
    
      @Pointcut("if()")
      public static boolean executingMethodMatches(JoinPoint.EnclosingStaticPart staticPart) {
        return staticPart.getSignature().toLongString().contains("de.scrum_master.app.Application.foo(int)");
      }
    
      @Before(
        "cflow(pointcutForSpecificMethod()) && set(* *) && " +
        "executingMethodMatches(thisEnclosingJoinPointStaticPart)"
      )
      public void beforeAdvice(JoinPoint thisJoinPoint, JoinPoint.EnclosingStaticPart thisEnclosingJoinPointStaticPart)
      {
        System.out.println(thisEnclosingJoinPointStaticPart);
        System.out.println(thisJoinPoint);
      }
    }
    

    I tried to keep the two aspects as similar as possible structurally. No matter which aspect syntax variant you choose, the output will be:

    execution(void de.scrum_master.app.Application.foo(int))
    set(int de.scrum_master.app.Application.field)