当前位置: 首页 > 知识库问答 >
问题:

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

尉迟明贤
2023-03-14

人们经常会问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);
  }
}

不幸的是,方面没有打印任何内容,就像类应用程序和方法two()没有任何标记一样。为什么AspectJ不拦截它们?


共有1个答案

柯镜
2023-03-14

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

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

永远不会被继承

  • 实现类,

注释继承仅适用于从类到子类,但仅当超类中使用的注释类型具有继承的元注释时,请参阅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(类型间定义)机制将它们添加回接口以及所有实现/重写类/方法。

现在,当运行应用程序时,控制台日志显示:

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:有人建议对后一种解决方案进行编辑,说嵌套在接口MyInterface中的方面MarkerAnNotationInheritor是隐式公共静态,因此可以省略方面声明中的修饰符。原则上这是真的,因为接口的成员(方法、嵌套类)默认情况下总是公共的,非静态内部类定义在接口内部也没有意义(没有实例可以绑定它)。不过,我喜欢在我的示例代码中明确,因为并非所有Java开发人员都知道这些细节。

此外,当前版本1.9.6中的AspectJ编译器在忽略静态时会抛出一个错误。我刚刚为这个问题创建了AspectJ问题#571104。

 类似资料:
  • 问题内容: 人们经常会问诸如此类的AspectJ问题,因此我想在一个我以后可以轻松链接的地方回答它。 我有这个标记注释: 现在,我注释这样的接口和/或方法: 这是一个小的驱动程序应用程序,它也实现了该接口: 现在,当我定义此方面时,我希望它会被触发 为每个构造函数执行带注释的类,并 每次执行带注释的方法。 不幸的是,方面没有打印任何内容,就像类Application和方法two()没有任何@Mar

  • 我有一个带有注释方法的接口。注释用标记,所以我希望实现者继承它。然而事实并非如此: 所以问题是,为什么没有,尽管它实现了一个标记为的方法,即?

  • 我对EasyMock(3.1)类的模拟有些困难。这应该适用于模拟部分类实现,我认为,这对于单元测试抽象基类,同时模拟缺少的方法是非常理想的。这是一个模式-一个立即识别的经典... 现在的测试是: EasyMock似乎不喜欢这样。它抛出: 有什么想法吗?我发现了一个相关的问题,但它并不是真正公正的标题。

  • 问题内容: 由于外部库不公开接口(因此不是可模拟的),而仅公开纯函数,因此我很难在Go中编写单元测试。即使像Google这样的大公司也没有,所以我想知道我的方法是否足够好。库不是提供s而不是仅提供函数的包以便用户模拟它们的好习惯吗? 到目前为止,我想到的解决方案是将这些程序包与接口的实现包装在一起,但这似乎工作量太大。 我举一个例子。我的功能可能看起来像这样 其中session是一个导入的包,返回

  • 我这里有我的问题的简化版本。A类有一个受保护的方法。类B继承了这个方法。 我现在用Mockito编写一个单元测试,它在另一个包测试中,我想测试方法。为此,我需要模拟getString()调用。由于该方法受到保护,并且我的测试类位于不同的包中,所以我不能使用。问题是,我监视类B。所以我不能使用。 我尝试通过反射获得受保护的方法: 但是我不知道如何在中使用这个。

  • 我有一个方法,我需要写单元测试用例。该方法返回类型。