So I have a custom annotation
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Intercepted {}
that I want to use to weave aspects into methods (AspectJ, @annotation(Intercepted)
).
The idea is that I weave the aspect in when I annotate the method @Intercepted
directly -- that part works -- or if I annotate the class, the aspect should be weaved into all its (public) methods -- that part doesn't.
Furthermore, if I annotate a class and one of its methods, the aspect should only get weaved in once, the method-level annotation overriding the class-level one.
Essentially, I want an "add the class-level annotation if there's a class-level annotation, but only if there isn't already a method-level annotation."
How do I do that?
Here is an AspectJ example. The pointcut syntax is the same in Spring AOP.
Helper classes:
package de.scrum_master.app;
import java.lang.annotation.*;
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Intercepted {}
package de.scrum_master.app;
@Intercepted
public class AnnotatedClass {
public void doSomething() {}
public void doSomethingElse() {}
}
package de.scrum_master.app;
public class AnnotatedMethod {
@Intercepted
public void doSomething() {}
public void doSomethingElse() {}
}
package de.scrum_master.app;
@Intercepted
public class AnnotatedMixed {
@Intercepted
public void doSomething() {}
public void doSomethingElse() {}
}
Driver application (Java SE, no Spring):
package de.scrum_master.app;
public class Application {
public static void main(String[] args) {
// Should be logged
new AnnotatedClass().doSomething();
// Should be logged
new AnnotatedClass().doSomethingElse();
// Should be logged
new AnnotatedMethod().doSomething();
// Should NOT be logged
new AnnotatedMethod().doSomethingElse();
// Should be logged, but only once
new AnnotatedMixed().doSomething();
// Should be logged
new AnnotatedMixed().doSomethingElse();
}
}
Aspect:
Please note that the execution(* *(..)) &&
part is not necessary in Spring AOP because only method execution joinpoints are supported there. The pointcut could just be annotatedMethod() || annotatedClass()
there. In AspectJ I have to be more precise because otherwise other joinpoint types would be logged.
package de.scrum_master.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class AnnotationInterceptor {
@Pointcut("@annotation(de.scrum_master.app.Intercepted)")
public void annotatedMethod() {}
@Pointcut("@within(de.scrum_master.app.Intercepted)")
public void annotatedClass() {}
@Before("execution(* *(..)) && (annotatedMethod() || annotatedClass())")
public void log(JoinPoint thisJoinPoint) {
System.out.println(thisJoinPoint);
}
}
Console log:
execution(void de.scrum_master.app.AnnotatedClass.doSomething())
execution(void de.scrum_master.app.AnnotatedClass.doSomethingElse())
execution(void de.scrum_master.app.AnnotatedMethod.doSomething())
execution(void de.scrum_master.app.AnnotatedMixed.doSomething())
execution(void de.scrum_master.app.AnnotatedMixed.doSomethingElse())
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With