javaspringaopspring-aoppointcut

How to write Pointcut expression for Spring AOP to weave meta-annotation in any level of deepness?


If I have Annotation:

@Retention(RUNTIME)
@Target({TYPE, METHOD})
public @interface Loggable {
  enum LogLevel {
    ERROR, WARN, INFO, DEBUG, TRACE;
  }

  LogLevel value() default DEBUG;
}

and trying to use it as target marker of Spring AOP aspect joinpoint marker, it works well for some aspect, weaved by that composite pointcut:

@Aspect
class LoggableAspect {
  @Pointcut("within(@Loggable *)")
  void aClass() {}

  //@Pointcut("@annotation(Loggable)") - in straight-forward case it works too!
  @Pointcut("execution(@Loggable * *(..))")
  void method() {}

  @Around("method() || aClass()")
  Object aroundAdvice(ProceedingJoinPoint pjp) {...}
}

In other words, it works well as "straight-forward" annotation usage, when I write code like this:

@Component
public class Abc {

  @Loggable
  public String method(int x) {
    return "string: " + x;
  }
}

But in case of META-annotation...:

@Loggable(INFO)
@Retention(RUNTIME)
public @interface Log {
}

...it doesn't work for example, in that code:

@Component
public class Abc {

  @Log // doesn't work! :(
  public String method(int x) {
    return "string: " + x;
  }
}

Certainly, I can write yet another pointcut for that particular case of 2-level deepness:

//...

@Pointcut("execution(@(@Loggable *) * *(..))")
void metaMethod() {}

@Around("method() || metaMethod() || aClass()")
Object aroundAdvice(ProceedingJoinPoint pjp) {...}

and it will work, but I want universal solution, working on any level of deepness - 3, 4, 5... Is it possible for that style of AOP?

P.S. That issue:

execution(public * ((@Transactional *)+).*(..))

looks like exactly right solution, but unfortunately, it doesn't work in my case. I think, it's possible only as AspectJ solution (in *.aj files) - not for Spring AOP. Am I right?..


Solution

  • The correct answer to your question, even though you might not like it, is: Neither Spring AOP as a syntactical subset of AspectJ nor native AspectJ provide a dedicated syntax to achieve what you wish to do. The best you can actually do is to use a nested syntax like you suggested yourself and use a reasonable number of nesting levels.

    In my answer here you find solutions for both class- and method-level meta annotations.


    Update: You misunderstood what

    execution(public * ((@Transactional *)+).*(..))
    

    means, even though the answer you linked to explains it:

    Matches the execution of any public method in a type with the Transactional annotation, or any subtype of a type with the Transactional annotation.

    So this syntax is about class inheritance, not about meta annotation nesting. It means that if you have @Transational class Parent, it would match class Child extends Parent and class Grandchild extends Child etc. The syntax should also work in Spring AOP, but that does not solve your problem.