aspectjspring-aoppointcutaspectsspring-aspects

Spring AOP : Interface method with Parameter annotation captured, but annotation is not present


I'm using Spring AOP to intercept a method execution.

I have an interface that looks like the following:

public interface MyAwesomeService {
    public Response doThings(int id, @AwesomeAnnotation SomeClass instance);
}

Here is the implementation of the interface:

public class MyAwesomeServiceImpl implements MyAwesomeService {
    public Response doThings(int id, SomeClass instance) {
        // do something.
    }
}

Now i would like any method which has a parameter annotated with @AwesomeAnnotation should be captured by Spring AOP.

So I wrote the following aspect which works.

@Aspect
@Component
public class MyAwesomeAspect {
    @Around("myPointcut()")
    public Object doAwesomeStuff(final ProceedingJoinPoint proceedingJoinPoint) {
         final MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
         Annotation[][] annotationMatrix = methodSignature.getMethod().getParameterAnnotations();

         // annotationMatrix is empty.
    }

    @Pointcut("execution(public * *(.., @package.AwesomeAnnotation  (package.SomeClass), ..))")
    public void myPointcut() {}
}

However when I try to find the parameter annotations I don't get any annotations back. As mentioned above, the annotationMatrix is empty.

So here are my questions:

  1. Why is the annotationMatrix empty? Probably because parameter annotations are not inherited from an interface.
  2. Why I'm able to capture the method execution. Since Spring AOP is able match the pointcut, Spring somehow is able to see the method's parameter annotations but when I try to see that using methodSignature.getMethod().getParameterAnnotations() it doesn't work.

Solution

  • The answers to your questions:

    1. Parameter annotations are not inherited from interfaces to implementing methods. In fact, annotations are almost never inherited, only from class (not interface!) to subclass if the annotation type itself is annotated by @Inherited, see JDK API documentation. Update: Because I have answered this question several times before, I have just documented the problem and also a workaround in Emulate annotation inheritance for interfaces and methods with AspectJ.

    2. Because during compile or weave time AspectJ can match your pointcut against the interface method and thus sees the annotation.

    You can fix the situation by adding the annotation to the parameter in your interface implementation, e.g. like this:

    @Override
    public Response doThings(int id, @AwesomeAnnotation SomeClass instance) {
        // ...
    }
    

    Then with an aspect like this...

    @Aspect
    @Component
    public class MyAwesomeAspect {
        @Pointcut("execution(public * *..MyAwesomeService.*(*, @*..AwesomeAnnotation (*), ..)) && args(*, instance, ..)")
        static void myPointcut(SomeClass instance) {}
    
        @Around("myPointcut(instance)")
        public Object doAwesomeStuff(Object instance, ProceedingJoinPoint proceedingJoinPoint) {
            System.out.println(proceedingJoinPoint);
            System.out.println("  instance = " + instance);
            MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
             Annotation[][] annotationMatrix = methodSignature.getMethod().getParameterAnnotations();
             for (Annotation[] annotations : annotationMatrix) {
                 for (Annotation annotation : annotations) {
                     System.out.println("  annotation = " + annotation);
                 }
             }
             return proceedingJoinPoint.proceed();
        }
    }
    

    ... you get a console log similar to this:

    execution(Response de.scrum_master.app.MyAwesomeServiceImpl.doThings(int, SomeClass))
      instance = de.scrum_master.app.SomeClass@23fc625e
      annotation = @de.scrum_master.app.AwesomeAnnotation()