java inheritance annotations aspectj

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



inheritance annotations (1)

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?


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