建议中无法识别 AspectJ 类型间字段

AspectJ inter-type field not recognized in advice

我实际上是在尝试跟踪一个帐户的转账次数 class。 在此处阅读文档:https://www.eclipse.org/aspectj/doc/released/progguide/language-anatomy.html 在幻灯片 48 和 49 上:https://www.eclipse.org/aspectj/doc/released/progguide/language-anatomy.html

这些告诉我我应该能够做这样的事情:

public aspect LoggingAspect {
    private int Account.transferCount = 0;
    private int Account.getTransferCount() {
        return transferCount;
    }

    pointcut firstTransfer(Account s, double amount):
        withincode(public void transfer (int, int, double))
            && call(public boolean withdraw(int,double))
                && target(s)
                    && args(amount);
    boolean around(Account s, double amount):
        firstTransfer(s, amount){
            s.transferCount++;     // Not recognized

            if (s.getTransferCount() == 0) {    // Not recognized
                System.out.println("50% markup");
                return s.deposit(amount*.5);
            }
            return false;
        }
}

但是,正如上面代码中所评论的,这些字段未被识别为存在于方面内的 class 上。我做错了什么?

我得到的错误是:transferCount cannot be resolved or is not a field

Account class 中发生了一些事情,不幸的是你没有在这里分享。请了解 MCVE 是什么以及始终提供一个如此有价值的原因。特别是在 AOP 的上下文中,它甚至更重要,因为没有目标 class 的方面没有多大意义。我无法在没有另一个的情况下调试一个,这就是为什么我必须发明自己的虚拟机 class。那实际上是你的工作。

可能您正在尝试直接从 Account class 中使用声明的私有成员。由于我还不明白的原因,这不起作用,因为它会抛出 AspectJ 编译器并显示 The method getTransferCount() from the type Account is not visible 或类似的错误消息。这一定是 AspectJ 中的限制或错误,我会询问维护者并稍后在此处报告。

但首先让我们重现您的情况:

申请class:

package de.scrum_master.app;

public class Account {
  public void transfer(int a, int b, double c) {
    withdraw(a, c);
  }

  public boolean withdraw(int a, double c) {
    return true;
  }

  public boolean deposit(double amount) {
    return true;
  }

  public static void main(String[] args) {
    Account account = new Account();
    account.transfer(11, 22, 33.33);
    account.withdraw(44, 55.55);
    account.transfer(66, 77, 88.88);
    account.withdraw(99, 11.11);

    // [error] The method getTransferCount() from the type Account is not visible
    System.out.println(account.getTransferCount());
  }
}

看点:

首先让我提一下,我修正了您代码中的两个错误:

  • 只有正确绑定参数,切入点才会匹配。 double amount 是两个方法参数中的第二个,而不是唯一的一个。因此你必须写 args(*, amount) 而不是 args(amount)

  • 您在 检查 s.getTransferCount() == 0 之前递增 transferCount ,因此 if 条件永远不会匹配。你要的是s.getTransferCount() == 1.

package de.scrum_master.aspect;

import de.scrum_master.app.Account;

public aspect LoggingAspect {
  private int Account.transferCount = 0;

  private int Account.getTransferCount() {
    return transferCount;
  }

  pointcut firstTransfer(Account s, double amount) :
    withincode(public void transfer (int, int, double)) &&
    call(public boolean withdraw(int, double)) &&
    target(s) &&
    args(*, amount);

  boolean around(Account s, double amount) : firstTransfer(s, amount) {
    s.transferCount++;
    if (s.getTransferCount() == 1) {
      System.out.println("50% markup");
      return s.deposit(amount * .5);
    }
    return false;
  }
}

现在在 Eclipse 中我看到应用程序中的编译错误 class 并且由于编译失败导致方面本身出现后续问题。一旦您注释掉 main 方法的最后一行,它就会起作用。 (也许你必须重新保存方面或重新编译项目才能使波浪线消失。)

实际上最简单的做法是将 getTransferCount() 设置为 public 而不是私有。 Getters 通常是 public 然后你也可以再次使用 main 方法中的方法,程序输出将变为:

50% markup
2

顺便说一句,在 aspect 里面你不需要使用 getTransferCount()。就像上面那行一样,可以直接访问该字段。


更新: 我答应过你一个问题的答案,为什么目标 class 不能通过 ITD 访问声明为 private 的字段和方法:因为它们方面 本身是私有的!本回答来自AspectJ维护者本人,请阅读full answer here.