After digging deeper into the topic of AOP, I finally found a working solution.
Originally, I'm using the following pointcuts.
@Aspect
@Component
public static class MyAnnotationAspect {
/**
* Matches the execution of any methods in a type annotated with @MyAnnotation.
*/
@Pointcut("execution(* (@com.test.MyAnnotation *).*(..))")
public void methodInMyAnnotationType() {}
/**
* Matches the execution of any methods annotated with @MyAnnotation.
*/
@Pointcut("execution(@com.test.MyAnnotation * *.*(..))")
public void methodAnnotatedWithMyAnnotation() {}
@Around("methodInMyAnnotationType() || methodAnnotatedWithMyAnnotation()")
public Object aop(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("AOP IS WORKING");
return pjp.proceed;
}
}
What I learned is that the methodInMyAnnotationType
pointcut will only work if I put @MyAnnotation
on the class that actually owns the method. However, if I put the annotation on class B that extends class A, the AOP cannot intercept methods from class A.
One potential solution I found is as following.
@Pointcut("execution(* *(..)) && @this(com.test.MyAnnotation)")
It means the pointcut is for ALL methods from current class AND parent class and the current class must be annotated with @MyAnnotation
. It looks promising. Unfortunately, Spring AOP doesn't support @this
pointcut primitive which produces UnsupportedPointcutPrimitiveException
.
After a bit more digging into the topic of this
, I found the existence of target
primitive and came up with the following solution.
@Pointcut("execution(@com.test.MyAnnotation * *.*(..))")
public void annotatedMethod() {}
@Pointcut("execution(* (@com.test.MyAnnotation *).*(..))")
public void annotatedClass() {}
@Pointcut("execution(* *(..)) && target(com.test.MyAnnotable)")
public void implementedInterface() {}
@Around("annotatedMethod() || annotatedClass() || implementedInterface()")
public Object aop(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("AOP IS WORKING");
return pjp.proceed;
}
It means the pointcut is for ALL methods from current class AND parent class. In addition, the method must be annotated with @MyAnnotation
or the class containing the method is annotated with @MyAnnotation
or the object that has this method must be an instance of the marker interface MyAnnotable
. It looks nice and it works.
My final class implementation looks like this.
@Service
public class ShiftModule {
@Service
public class Annotated extends ShiftModule implements MyAnnotable {}
@Resource
private ShiftModule.Annotated self;
}
Add-on information:
I did give the following pointcut a try during my experimentation.
@Pointcut("@annotation(com.test.MyAnnotation)")
public void annotatedMethod() {}
@Pointcut("@within(com.test.MyAnnotation)")
public void annotatedClass() {}
@Pointcut("target(com.test.MyAnnotable)")
public void implementedInterface() {}
@Around("execution(* *(..)) && (annotatedMethod() || annotatedClass() || implementedInterface()")
public Object aop(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("AOP IS WORKING");
return pjp.proceed;
}
What I found is it does NOT work with annotated inner interface, meaning the code below will stop working. The AOP aspect doesn't have any effects at all.
@Repository
public interface OrderRepo extends JpaRepository<Order,Long> {
@Repository("annotatedOrderRepo")
@MyAnnotation
public interface Annotated extends OrderRepo {}
}