8wDlpd.png
8wDFp9.png
8wDEOx.png
8wDMfH.png
8wDKte.png

使用 AspectJ 模拟接口和方法的注释继承

dtejada 2月前

48 0

人们经常会问类似这样的 AspectJ 问题,所以我想在一个以后可以轻松链接到的地方回答它。我有这个标记注释:package de.scrum_master.app; import java.lang.annot...

人们经常会问类似这样的 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 不拦截它们?

帖子版权声明 1、本帖标题:使用 AspectJ 模拟接口和方法的注释继承
    本站网址:http://xjnalaquan.com/
2、本网站的资源部分来源于网络,如有侵权,请联系站长进行删除处理。
3、会员发帖仅代表会员个人观点,并不代表本站赞同其观点和对其真实性负责。
4、本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
5、站长邮箱:yeweds@126.com 除非注明,本帖由dtejada在本站《spring》版块原创发布, 转载请注明出处!
最新回复 (0)
  • 这里的问题不是 AspectJ,而是 JVM。在 Java 中,

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

    永远不会继承

    • 实现类,
    • 覆盖方法或
    • 使用带注释的注解的类。

    注释继承仅适用于从类到子类,但前提是超类中使用的注释类型带有元注释 @Inherited ,请参阅 JDK JavaDoc .

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

    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(类型间定义)机制将它们添加回接口以及所有实现/覆盖类/方法。

    现在运行时控制台日志 Application 显示:

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

    顺便说一句,您也可以将方面直接嵌入到界面中,以便将所有内容放在一个地方。只需小心重命名, MyInterface.java MyInterface.aj 帮助 AspectJ 编译器识别出它必须在此处执行一些工作。

    package de.scrum_master.app;
    
    public interface MyInterface {
      void one();
      void two();
    
      // Cannot omit 'static' here due to https://bugs.eclipse.org/bugs/show_bug.cgi?id=571104 
      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;
      }
    }
    

    更新 2021-02-11: 有人建议对后一种解决方案进行修改,称 MarkerAnnotationInheritor 接口内部嵌套的 MyInterface 是隐式的 public static ,因此可以省略方面声明中的修饰符。原则上这是正确的,因为接口的成员(方法、嵌套类)默认情况下始终是公共的,并且非静态内部类定义在接口内部也没有意义(没有实例可以将其绑定到)。不过,我喜欢在示例代码中明确说明,因为并非所有 Java 开发人员都知道这些细节。

    此外,目前版本 1.9.6 中的 AspectJ 编译器如果我们省略则会抛出错误 static 。我刚刚 针对此问题 AspectJ 问题 #571104

  • 在您的示例中,您的方面专门处理名为“two”的方法。它特定于“two”,不能重复用于其他情况。有没有办法选择任何已使用某些注释进行注释的方法,而不是直接通过名称提及它们?

  • 当然。问题和答案仅涉及注释接口的选定方法,而不是所有方法。如果您有任何具体问题,请发布新问题并通知我。这里的评论功能不是讨论不相关问题的正确方法。向 MCVE 提出一个具体的问题,然后我可以看看是否可以帮助您。我现在很忙,所以我不喜欢反复提问和回答,只是为了找出你真正想知道的东西。

  • @kriegaex 我相信你就是那个可以帮助我的人。你能看一下这个问题吗?com/questions/50391609/…?

返回
作者最近主题: