java - Emule la herencia de anotaciones para interfaces y métodos con AspectJ



inheritance annotations (1)

El problema aquí no es AspectJ sino la JVM. En Java, las anotaciones en

  • interfaces,
  • métodos o
  • otras anotaciones

nunca son heredados por

  • implementando clases,
  • métodos de anulación o
  • clases usando anotaciones anotadas.

La herencia de anotación solo funciona de clases a subclases, pero solo si el tipo de anotación utilizado en la superclase lleva la anotación meta @Inherited , consulte JDK JavaDoc .

AspectJ es un lenguaje JVM y, por lo tanto, funciona dentro de las limitaciones de la JVM. No hay una solución general para este problema, pero para interfaces o métodos específicos para los que desea emular la herencia de anotaciones, puede usar una solución 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
 * <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;
}

Tenga en cuenta: Con este aspecto en su lugar, puede eliminar las anotaciones (literales) de la interfaz y del método anotado porque la mecánica ITD (definición inter-tipo) de AspectJ las agrega de nuevo a la interfaz y a todas las clases / métodos de implementación / anulación .

Ahora el registro de la consola al ejecutar la Application dice:

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

Por cierto, también podría incrustar el aspecto directamente en la interfaz para tener todo en un solo lugar. Solo tenga cuidado de cambiar el nombre de MyInterface.java a MyInterface.aj para ayudar al compilador de AspectJ a reconocer que tiene que hacer algo de trabajo aquí.

package de.scrum_master.app;

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

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

A menudo, las personas hacen preguntas de AspectJ como esta, por lo que quiero responderlas en un lugar al que pueda vincular fácilmente más adelante.

Tengo esta anotación 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 {}

Ahora anoto una interfaz y / o métodos como este:

package de.scrum_master.app;

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

Aquí hay una pequeña aplicación de controlador que también implementa la interfaz:

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();
  }
}

Ahora, cuando defino este aspecto, espero que se active

  • para cada ejecución del constructor de una clase anotada y
  • para cada ejecución de un 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);
  }
}

Desafortunadamente, el aspecto no imprime nada, como si la clase Application y el método two() no tuvieran ninguna anotación @Marker . ¿Por qué AspectJ no los intercepta?





aspectj