使用AspectJ模拟接口和方法的注解inheritance

通常人们会问这样的AspectJ问题,所以我想在稍后可以轻松链接的地方回答。

我有这个标记注释:

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

现在我注释一个接口和/或像这样的方法:

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

这里是一个小驱动程序的应用程序,它也实现了接口:

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

现在当我定义这个方面时,我期望它被触发

  • 为每个构造函数执行一个带注释的类和
  • 用于每个执行注释的方法。
 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); } } 

不幸的是该方面没有打印任何东西,就好像类Application和方法two()没有任何@Marker注释一样。 AspectJ为什么不拦截它们?

这里的问题不是AspectJ,而是JVM。 在Java中,注释

  • 接口,
  • 方法或
  • 其他注释

永远不会被遗传

  • 实施课程,
  • 压倒一切的方法或
  • 使用注释注释的类。

注释inheritance只能从类到子类,但是只有在超类中使用的注释types带有元注释@Inherited ,请参阅JDK JavaDoc 。

AspectJ是一种JVM语言,因此可以在JVM的限制内工作。 这个问题没有通用的解决scheme,但是对于希望模拟注解inheritance的特定接口或方法,可以使用如下的解决方法:

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

请注意:有了这个方面,你可以从接口和注释方法中删除(文字)注释,因为AspectJ的ITD(types间定义)机制将它们添加回接口加上所有的实现/覆盖类/方法。

现在运行Application的控制台日志说:

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

顺便说一下,您还可以将界面embedded到界面中,以便将所有内容都集中在一起。 只需要小心地将MyInterface.java重命名为MyInterface.aj ,以便帮助AspectJ编译器识别它在这里需要做的一些工作。

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