AspectJ 方面是单例吗?

Are AspectJ aspects Singletons?

使用 Aspect annotation as described here 创建方面时,是否可以将此注释与包含状态的 class 一起使用(例如,一旦命中切入点就会更改的成员变量)?或者换句话说:方面 class 是单例吗?注释的源代码建议类似这样,但我无法在文档中找到更多详细信息。

举个例子:这是一个计算方法被调用频率的方面。方面将调用次数存储在映射中并打印出映射:

@Aspect
public class CountAspect {

    private Map<String, Integer> counter = new HashMap<>(); 

    @Before("execution(* *.*(..))")
    public void countMethodCalls(JoinPoint jp) {
        String methodName = jp.getSignature().getName();
        counter.merge(methodName, 1, Integer::sum);
        System.out.println(counter);
    }
}

测试这方面时,一切都按预期工作。多次调用方法abc时,输出为

{main=1}
{a=1, main=1}
{a=1, b=1, main=1}
{a=1, b=1, c=1, main=1}
{a=2, b=1, c=1, main=1}
{a=3, b=1, c=1, main=1}

这个结果靠谱吗?还是我的示例只起作用,因为我这里有一个相当简单的示例?

在更复杂的情况下,AspectJ 运行时会创建 CountAspect 的第二个实例,因此我会丢失在 counter 地图中收集的数据吗?

如果重要的话,我正在使用加载时编织。

AspectJ 知道多种实例化模型,其中之一(默认)是单例。您可以在 AspectJ manual 中找到更多信息。在那里您将看到以下安装模型:

  • perthis:每个匹配切入点调用者实例一个方面实例
  • pertarget:每个匹配切入点被调用者实例的一个方面实例
  • percflow:每进入某个控制流一个方面实例
  • percflowbelow:每进入一个方面实例一个特定的控制流低于被拦截的(被拦截的方法调用的一切)

描述了另一种实例化模型here

  • pertypewithin:切入点
  • 指定的每个类型(class)一个方面实例

所以是的,示例代码中显示的方面是单例,您可以依赖它。无论您使用的是编译时织入、二进制织入还是加载时织入都无关紧要,除非您在像应用程序服务器这样的容器中通过 class-loader 隔离等方式故意创建不同的织入器实例。