Search code examples
javaaspectjpointcut

Expression for Parametrized annotation AspectJ


Trying to created a point cut which take parameter from annotation and then can use it further.So far I have reached:

pointcut callDemoAspectPointCut():
      call(Papa+.new()) && @within(MyAnnotation); //With param here

   after() returning(Object r) :callDemoAspectPointCut(){//use param here
      sysout("executed");
}

Please advice ..


Solution

  • There are several kinds of annotations you can capture:

    • class annotations
    • method annotations
    • member annotations
    • method parameter annotations

    Here is an example for each:

    Marker annotation:

    package de.scrum_master.app;
    
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyAnnotation {
        int id();
        String name();
    }
    

    Driver application using annotations in different places:

    package de.scrum_master.app;
    
    @MyAnnotation(id = 1, name = "class")
    public class Application {
        @MyAnnotation(id = 2, name = "member")
        private String member = "foo";
    
        @MyAnnotation(id = 3, name = "method")
        public static void main(String[] args) {
            new Application().doSomething("blah", Math.PI);
        }
    
        private String doSomething(String text, @MyAnnotation(id = 4, name = "parameter") double number) {
            String returnValue = member + " " + number;
            member = text;
            return returnValue;
        }
    }
    

    Aspect capturing annotations:

    Most of the pointcut/advice pairs are quite elegant. But unfortunately you need some rather ugly reflection to get hold of parameter annotations.

    package de.scrum_master.aspect;
    
    import java.lang.annotation.Annotation;
    import java.lang.reflect.Method;
    
    import org.aspectj.lang.SoftException;
    import org.aspectj.lang.reflect.MethodSignature;
    
    import de.scrum_master.app.MyAnnotation;
    
    public aspect AnnotationParameterAspect {
        pointcut methodExecutionInAnnotatedClass(MyAnnotation myAnnotation) :
            @within(myAnnotation) && execution(* *(..));
    
        pointcut annotatedMemberReadAccess(MyAnnotation myAnnotation) :
            @annotation(myAnnotation) && get(* *);
    
        pointcut annotatedMemberWriteAccess(MyAnnotation myAnnotation) :
            @annotation(myAnnotation) && set(* *);
    
        pointcut annotatedMethodExecution(MyAnnotation myAnnotation) :
            @annotation(myAnnotation) && execution(* *(..));
    
        pointcut annotatedMethodParameter() :
            execution(* *(.., @MyAnnotation (*), ..));
    
        after(MyAnnotation myAnnotation) returning(Object returnValue) :
            methodExecutionInAnnotatedClass(myAnnotation)
        {
            System.out.println(thisJoinPoint + " -> " + returnValue);
            printAnnotation(myAnnotation);
        }
    
        after(MyAnnotation myAnnotation) returning(Object returnValue) :
            annotatedMemberReadAccess(myAnnotation)
        {
            System.out.println(thisJoinPoint + " -> " + returnValue);
            printAnnotation(myAnnotation);
        }
    
        after(MyAnnotation myAnnotation, Object newValue) :
            annotatedMemberWriteAccess(myAnnotation) && args(newValue)
        {
            System.out.println(thisJoinPoint + " -> " + newValue);
            printAnnotation(myAnnotation);
        }
    
        after(MyAnnotation myAnnotation) returning(Object returnValue) :
            annotatedMethodExecution(myAnnotation)
        {
            System.out.println(thisJoinPoint + " -> " + returnValue);
            printAnnotation(myAnnotation);
        }
    
        after() : annotatedMethodParameter() {
            System.out.println(thisJoinPoint);
            MethodSignature methodSignature = (MethodSignature) thisJoinPoint.getSignature();
            Class<?> clazz = methodSignature.getDeclaringType();
            try {
                Method method = clazz.getDeclaredMethod(methodSignature.getName(), methodSignature.getParameterTypes());
                for (Annotation[] parameterAnnotations : method.getParameterAnnotations()) {
                    for (Annotation annotation : parameterAnnotations) {
                        if (annotation instanceof MyAnnotation)
                            printAnnotation((MyAnnotation) annotation);
                    }
                }
            }
            catch (NoSuchMethodException nsme) {
                throw new SoftException(nsme);
            }
        }
    
        private static void printAnnotation(MyAnnotation myAnnotation) {
            System.out.println("  " + myAnnotation);
            System.out.println("    id   = " + myAnnotation.id());
            System.out.println("    name = " + myAnnotation.name() + "\n");
        }
    }
    

    Console log:

    Please note how annotations in different places are logged along with their parameter values:

    set(String de.scrum_master.app.Application.member) -> foo
      @de.scrum_master.app.MyAnnotation(id=2, name=member)
        id   = 2
        name = member
    
    get(String de.scrum_master.app.Application.member) -> foo
      @de.scrum_master.app.MyAnnotation(id=2, name=member)
        id   = 2
        name = member
    
    set(String de.scrum_master.app.Application.member) -> blah
      @de.scrum_master.app.MyAnnotation(id=2, name=member)
        id   = 2
        name = member
    
    execution(String de.scrum_master.app.Application.doSomething(String, double)) -> foo 3.141592653589793
      @de.scrum_master.app.MyAnnotation(id=1, name=class)
        id   = 1
        name = class
    
    execution(String de.scrum_master.app.Application.doSomething(String, double))
      @de.scrum_master.app.MyAnnotation(id=4, name=parameter)
        id   = 4
        name = parameter
    
    execution(void de.scrum_master.app.Application.main(String[])) -> null
      @de.scrum_master.app.MyAnnotation(id=1, name=class)
        id   = 1
        name = class
    
    execution(void de.scrum_master.app.Application.main(String[])) -> null
      @de.scrum_master.app.MyAnnotation(id=3, name=method)
        id   = 3
        name = method