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