8

Often people ask AspectJ questions like this one, so I want to answer it in a place I can easily link to later.

I have this marker annotation:

package de.scrum_master.app;

import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface Marker {}

Now I annotate an interface and/or methods like this:

package de.scrum_master.app;

@Marker
public interface MyInterface {
  void one();
  @Marker void two();
}

Here is a little driver application which also implements the interface:

package de.scrum_master.app;

public class Application implements MyInterface {
  @Override
  public void one() {}

  @Override
  public void two() {}

  public static void main(String[] args) {
    Application application = new Application();
    application.one();
    application.two();
  }
}

Now when I define this aspect, I expect that it gets triggered

  • for each constructor execution of an annotated class and
  • for each execution of an annotated method.
package de.scrum_master.aspect;

import de.scrum_master.app.Marker;

public aspect MarkerAnnotationInterceptor {
  after() : execution((@Marker *).new(..)) && !within(MarkerAnnotationInterceptor) {
    System.out.println(thisJoinPoint);
  }

  after() : execution(@Marker * *(..)) && !within(MarkerAnnotationInterceptor) {
    System.out.println(thisJoinPoint);
  }
}

Unfortunately the aspect prints nothing, just as if class Application and method two() did not have any @Marker annotation. Why does AspectJ not intercept them?

kriegaex
  • 63,017
  • 15
  • 111
  • 202

1 Answers1

11

The problem here is not AspectJ but the JVM. In Java, annotations on

  • interfaces,
  • methods or
  • other annotations

are never inherited by

  • implementing classes,
  • overriding methods or
  • classes using annotated annotations.

Annotation inheritance only works from classes to subclasses, but only if the annotation type used in the superclass bears the meta annotation @Inherited, see JDK JavaDoc.

AspectJ is a JVM language and thus works within the JVM's limitations. There is no general solution for this problem, but for specific interfaces or methods you wish to emulate annotation inheritance for, you can use a workaround like this:

package de.scrum_master.aspect;

import de.scrum_master.app.Marker;
import de.scrum_master.app.MyInterface;

/**
 * It is a known JVM limitation that annotations are never inherited from interface
 * to implementing class or from method to overriding method, see explanation in
 * <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/annotation/Inherited.html">JDK API</a>.
 * <p>
 * Here is a little AspectJ trick which does it manually.
 *
 */
public aspect MarkerAnnotationInheritor {
  // Implementing classes should inherit marker annotation
  declare @type: MyInterface+ : @Marker;
  // Overriding methods 'two' should inherit marker annotation
  declare @method : void MyInterface+.two() : @Marker;
}

Please note: With this aspect in place, you can remove the (literal) annotations from the interface and from the annotated method because AspectJ's ITD (inter-type definition) mechanics adds them back to the interface plus to all implementing/overriding classes/methods.

Now the console log when running the Application says:

execution(de.scrum_master.app.Application())
execution(void de.scrum_master.app.Application.two())

By the way, you could also embed the aspect right into the interface so as to have everything in one place. Just be careful to rename MyInterface.java to MyInterface.aj in order to help the AspectJ compiler to recognise that it has to do some work here.

package de.scrum_master.app;

public interface MyInterface {
  void one();
  void two();

  // Cannot omit 'static' here due to https://bugs.eclipse.org/bugs/show_bug.cgi?id=571104 
  public static aspect MarkerAnnotationInheritor {
    // Implementing classes should inherit marker annotation
    declare @type: MyInterface+ : @Marker;
    // Overriding methods 'two' should inherit marker annotation
    declare @method : void MyInterface+.two() : @Marker;
  }
}

Update 2021-02-11: Someone suggested an edit to the latter solution, saying that the aspect MarkerAnnotationInheritor nested inside interface MyInterface is implicitly public static, so the modifiers in the aspect declaration could be omitted. In principle this is true, because members (methods, nested classes) of interfaces are always public by default and a non-static inner class definition would not make sense inside an interface either (there is no instance to bind it to). I like to be explicit in my sample code, though, because not all Java developers might know these details.

Furthermore, currently the AspectJ compiler in version 1.9.6 throws an error if we omit static. I have just created AspectJ issue #571104 for this problem.

kriegaex
  • 63,017
  • 15
  • 111
  • 202
  • Thanks. Could that be enhanced to dynamically discover the annotated methods? – NeilS Apr 21 '17 at 09:13
  • @NeilS, I do not understand the question. – kriegaex May 31 '17 at 03:57
  • In your example your aspect specifically handles method named 'two'. It's specific to 'two' and not reusable to other situations. Is there a way to select any methods which have been annotated with some annotation rather than mentioning them directly by name? – NeilS Jun 02 '17 at 10:08
  • Of course. The question and answer were about only selected methods of an interface being annotated, not all of them. If you have any specific problem, please post a new question and notify me. The comment functionality here is not the right way to discuss unrelated questions. Present an [MCVE](http://stackoverflow.com/help/mcve) with a concrete problem, then I can see if I can help you. I am quite busy currently, so I do not like many iterations of questions and answers just so as to find out what you actually want to know. – kriegaex Jun 02 '17 at 11:07
  • @kriegaex I believe you're the one who can help me. Could you please tale a look at this question https://stackoverflow.com/questions/50391609/aspectj-custom-aj-file-is-ignored? – VB_ May 17 '18 at 12:35