20

How can I write an aspectj pointcut that applies to method executions which override an interface method with an annotation? For example:

interface A {
  @MyAnnotation void method();
}
class B implements A {
  void method();
}

The pointcut execution(@MyAnnotation * *.*(..)) does only match if B.method() carries the annotation itself. Is there another way to do this?

soc
  • 27,983
  • 20
  • 111
  • 215
Dr. Hans-Peter Störr
  • 25,298
  • 30
  • 102
  • 139
  • I am facing similar issue with JAX-RS Annotation javax.ws.rs.Path. All Path of resources in my project are defined in interface and I want to pointcut it. I am failing miserably till now. – Deepak Negi May 09 '21 at 14:52

3 Answers3

12

As Nicholas pointed out, this is not possible in AspectJ. Here is more proof of why it is not possible (taken from http://www.eclipse.org/aspectj/doc/released/adk15notebook/annotations-pointcuts-and-advice.html section Annotation Inheritance and pointcut matching):

According to the Java 5 specification, non-type annotations are not inherited, and annotations on types are only inherited if they have the @Inherited meta-annotation. The call to c2.aMethod (that would be b.method() in your example) is not matched because join point matching for modifiers (the visibility modifiers, annotations, and throws clause) is based on the subject of the join point (the method actually being called).

Having suffered from this same issue, I have written a small method/library that would allow you to write pointcuts for such methods. Here is how it would work for your example:

myAnnotationIsExecuted(): execution(public * *.*(..)) && 
             if(implementsAnnotation("@MyAnnotation()", thisJoinPoint));

OR

myAnnotationIsExecuted(): execution(public * A+.*(..)) &&
             if(implementsAnnotation("@MyAnnotation()", thisJoinPoint));

The method implementsAnnotation(String,JoinPoint) is coming from the library; a basic method that checks if the implemented methods contain the specified annotation.

More information about the method/library can be found here.

Guven
  • 2,280
  • 2
  • 20
  • 34
  • can you please give the example or which library has this implementsAnnotation(String,JoinPoint) because the link you have mentioned is not available. If anyone has any idea, please share here. – bharathi Dec 08 '22 at 10:32
  • @bharathi You can find the details here: https://medium.com/@guvenjamesb/intercepting-inherited-annotations-with-aspectj-published-september-2011-7dd0a974e141 (link in the original post has expired). – Guven Dec 09 '22 at 11:27
4

Update

Since it appears that it cannot be done in Spring (see original answer) I would say that you need to add the annotation to each method on the implementing classes. There is an obvious way to do this, but I suppose if you are looking for a more automatic way you will need to write one yourself.

I can imagine some sort of home-grown post-processor that will look for a specific annotation, lets say @ApplyThisAnnotationToAllWhoInheritMe. Once it finds this annotation, it would scan sourcecode for inheriting members and add the requested annotation where necessary before compilation happens.

Regardless, if you are using Spring AOP to do this, it appears that it needs to be on the implementers method. If the classes you are hoping this will help are out of your control, you may have to write your own Spring AOP tool.

Original Answer

From the Spring 3.0 Documentation:

AspectJ follows Java's rule that annotations on interfaces are not inherited.

I found this bit in section 7.8.2 Other Spring aspects for AspectJ, which is a part of 7.8 Using AspectJ with Spring applications, which makes me think this is a global rule.

nicholas.hauschild
  • 42,483
  • 9
  • 127
  • 120
  • 1
    Thanks; that explains why my pointcut does not work. However, I'm looking for one that works. :-) – Dr. Hans-Peter Störr Aug 24 '11 at 16:31
  • @hstoerr: After you edited your question, I edited my answer. Let me know if it is still unacceptable. – nicholas.hauschild Aug 24 '11 at 17:07
  • 1
    The spring documentation does not actually say it is impossible to match on something like what I want - just that the simple approach I tried does not work. There might be some weird thing one can do in Aspectj that I cannot think of. So I'm still hoping. :-) – Dr. Hans-Peter Störr Aug 24 '11 at 17:17
  • 1
    Would it be acceptable to go by the name of the method? That will be required to propagate to the implementors, and is something that can be achieved via AspectJ. – nicholas.hauschild Aug 24 '11 at 17:22
2
//introducing empty marker interface
declare parents : hasmethod(@MyAnnotation * *(..)) implements TrackedParentMarker;

public pointcut p1() : execution(* TrackedParentMarker+.*(..));

Also you should enable –XhasMember compiler flag.

alehro
  • 2,198
  • 2
  • 25
  • 41
  • Unfortunately that does not quite do it. It applies to every method of all classes that implement an interface which has at least one method that carries the annotation. But it should apply only to the one method that implements the interface method with that marker. Or at least to methods that have the same name with the marked interface method, if that's possible to say in AspectJ. – Dr. Hans-Peter Störr Aug 25 '11 at 12:59
  • @alehro how you configured your application to use *.aj files? Could you please take a look at this question https://stackoverflow.com/questions/50391609/aspectj-custom-aj-file-is-ignored? – VB_ May 17 '18 at 14:11