Emular a inheritance de annotations para interfaces e methods com o AspectJ

Muitas vezes as pessoas fazem perguntas da AspectJ como esta, então eu quero respondê-la em um lugar que eu possa ligar facilmente para mais tarde.

Eu tenho essa anotação de marcador:

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 {} 

Agora eu anoto uma interface e / ou methods como este:

 package de.scrum_master.app; @Marker public interface MyInterface { void one(); @Marker void two(); } 

Aqui está uma pequena aplicação de driver que também implementa a 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(); } } 

Agora, quando eu definir esse aspecto, espero que ele seja acionado

  • para cada execução de construtor de uma class anotada e
  • para cada execução de um método anotado.
 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); } } 

Infelizmente, o aspecto não imprime nada, como se a class Application e o método two() não tivessem nenhuma anotação @Marker . Por que o AspectJ não os intercepta?

O problema aqui não é AspectJ, mas a JVM. Em Java, annotations em

  • interfaces,
  • methods ou
  • outras annotations

nunca são herdados por

  • implementando classs,
  • methods primordiais ou
  • classs usando annotations anotadas.

A inheritance de annotations só funciona de classs para subclasss, mas somente se o tipo de anotação usado na superclass tiver a meta anotação @Inherited , consulte JDK JavaDoc .

AspectJ é uma linguagem de JVM e, portanto, trabalha dentro das limitações da JVM. Não há uma solução geral para esse problema, mas para interfaces específicas ou methods para os quais você deseja emular a inheritance de annotations, é possível usar uma solução alternativa como esta:

 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 * JDK API. * 

* Here is a little AspectJ trick which does it manually. * */ public aspect MarkerAnnotationInheritor { // Implementing classs should inherit marker annotation declare @type: MyInterface+ : @Marker; // Overriding methods 'two' should inherit marker annotation declare @method : void MyInterface+.two() : @Marker; }

Nota: Com este aspecto em vigor, você pode remover as annotations (literais) da interface e do método anotado porque a mecânica ITD (definição inter-tipos) de AspectJ as adiciona de volta à interface mais a todas as classs / methods de implementação / sobreposição .

Agora, o log do console ao executar o Application diz:

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

By the way, você também pode incorporar o aspecto direito na interface, de modo a ter tudo em um só lugar. Apenas MyInterface.java cuidado para renomear MyInterface.java para MyInterface.aj para ajudar o compilador AspectJ a reconhecer que ele tem que fazer alguma coisa aqui.

 package de.scrum_master.app; public interface MyInterface { void one(); void two(); public static aspect MarkerAnnotationInheritor { // Implementing classs should inherit marker annotation declare @type: MyInterface+ : @Marker; // Overriding methods 'two' should inherit marker annotation declare @method : void MyInterface+.two() : @Marker; } } 
    Intereting Posts