How about this?
@Pointcut(
"within(@org.springframework.stereotype.Controller *) || " +
"within(@org.springframework.web.bind.annotation.RestController *)" +
)
Or a little shorter, but maybe too fuzzy if there are other classes with matching names in Springs' packages (I have not checked):
@Pointcut("within(@(org.springframework..*Controller) *)")
Update: As for your real question, I understand it now after your edit. This is also possible, but kind of tricky syntactically. Let me rename your annotations into MetaAnnotation
and MyAnnotation
, okay? Because they are not really parent and child of each other, there is no inheritance involved in the the OOP sense, just nesting.
Annotations:
Please make sure that the annotations have indeed runtime scope. I did not see that in your code.
package de.scrum_master.app;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Retention(RUNTIME)
@Target({ TYPE })
public @interface MetaAnnotation {}
package de.scrum_master.app;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Retention(RUNTIME)
@Target({ TYPE })
@MetaAnnotation
public @interface MyAnnotation {}
Java sample classes:
One class is annotated with the meta annotation, one with the annotated annotation and one with no annotation (negative test case):
package de.scrum_master.app;
@MetaAnnotation
public class MyClassA {
public void doSomething() {}
}
package de.scrum_master.app;
@MyAnnotation
public class MyClassB {
public void doSomething() {}
}
package de.scrum_master.app;
public class MyClassC {
public void doSomething() {}
}
Driver application:
Because I am using pure Java + AspectJ without Spring, I am using this little application in order to demonstrate the result.
package de.scrum_master.app;
public class Application {
public static void main(String[] args) {
new MyClassA().doSomething();
new MyClassB().doSomething();
new MyClassC().doSomething();
}
}
Aspect:
package de.scrum_master.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class MetaAnnotationInterceptor {
@Before(
"execution(* *(..)) && (" +
"within(@de.scrum_master.app.MetaAnnotation *) || " +
"within(@(@de.scrum_master.app.MetaAnnotation *) *)" +
")"
)
public void myAdvice(JoinPoint thisJoinPoint){
System.out.println(thisJoinPoint);
}
}
Console log:
execution(void de.scrum_master.app.MyClassA.doSomething())
execution(void de.scrum_master.app.MyClassB.doSomething())
Now if you want to add yet another level of nesting, add a new annotation and annotate the formerly unannotated class with it:
package de.scrum_master.app;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Retention(RUNTIME)
@Target({ TYPE })
@MyAnnotation
public @interface MyOtherAnnotation {}
package de.scrum_master.app;
@MyOtherAnnotation
public class MyClassC {
public void doSomething() {}
}
Then extend the pointcut by one more level of nesting/recursion:
package de.scrum_master.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class MetaAnnotationInterceptor {
@Before(
"execution(* *(..)) && (" +
"within(@de.scrum_master.app.MetaAnnotation *) || " +
"within(@(@de.scrum_master.app.MetaAnnotation *) *) || " +
"within(@(@(@de.scrum_master.app.MetaAnnotation *) *) *)" +
")"
)
public void myAdvice(JoinPoint thisJoinPoint){
System.out.println(thisJoinPoint);
}
}
The console log changes to:
execution(void de.scrum_master.app.MyClassA.doSomething())
execution(void de.scrum_master.app.MyClassB.doSomething())
execution(void de.scrum_master.app.MyClassC.doSomething())
P.S.: The execution(* *(..))
part is only necessary in AspectJ in order to limit pointcut matching to method executions because AspectJ can intercept more events than Spring AOP. So in Spring AOP you can eliminate that part and the braces surrounding the ... || ... || ...
part.