I need to define a point cut which triggers the execution on all methods of a spring service annotated with a custom annotation. The annotation I would like to define the point cut on will be on an other annotation.
@Y
public @interface X {
}
and then the service would be annotated as following
@X
public Service1 {
}
I tried with the following point cut definition but it only works when @Y is on the service itself, meaning that it doesn't see that the annotation is on @X
@Around("@within(com.mypackage.Y)")
This is not a Spring or AspectJ problem. In Java, annotations on interfaces, other annotations or methods are never inherited by implementing classes, classes using annotated annotations or overriding methods. Annotation inheritance only works from classes onto subclasses, but only if the annotation type used in the superclass bears the meta annotation @Inherited
.
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.
Here is a little proof that what you want does not work and thus also cannot be utilised by an aspect:
package de.scrum_master.app;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface OuterAnnotation {}
package de.scrum_master.app;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@OuterAnnotation
public @interface InnerAnnotation {}
package de.scrum_master.app;
import java.lang.annotation.Annotation;
@InnerAnnotation
public class Application {
public static void main(String[] args) {
for (Annotation annotation : Application.class.getAnnotations())
System.out.println(annotation);
}
}
The console output shows that only the inner annotation is seen by the JVM, not the outer one used on the inner annotation:
@de.scrum_master.app.InnerAnnotation()
Update: Intrigued by Bradley M Handy's answer, I re-checked if it would also work for the situation described in my code, and indeed it does. Thise type of AspectJ syntax was unknown to me, even though I think I know a lot about AspectJ. So thanks, Bradley. :-) This aspect would work:
package de.scrum_master.aspect;
import de.scrum_master.app.OuterAnnotation;
public aspect MetaAnnotationAspect {
after() : within(@(@OuterAnnotation *) *) && execution(* *(..)) {
System.out.println(thisJoinPoint);
}
}
Console log when running the application:
@de.scrum_master.app.InnerAnnotation()
execution(void de.scrum_master.app.Application.main(String[]))
I had this exact need in an application. I found this answer, but wasn't satisfied this couldn't be done.
After a bit more searching, I found this cheat sheet for AspectJ/Spring pointcut expressions. The solution in the cheat sheet didn't work exactly as advertised, but I was able to make it work for what I needed.
@Pointcut("within(@(@Annotation *) *)")
public void classAnnotatedWithNestedAnnotationOneLevelDeep() { }
I combined this expression with a @within
expression for just the @Annotation
to get what I wanted to work.
For method execution:
@Pointcut("execution(@(@com.someorg.SomeAnnotation *) * *(..))")
public void methodAnnotatedWithNestedAnnotationOneLevelDeep() { }
I combined this expression with a @annotation
expression for just the @Annotation
to get what I wanted to work for methods.