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?..
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 theTransactional
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.