问题
I'm essentially trying to track the number of transfers for an Account class. Reading the docs here: https://www.eclipse.org/aspectj/doc/released/progguide/language-anatomy.html And on slide 48 and 49 here: https://www.eclipse.org/aspectj/doc/released/progguide/language-anatomy.html
These tell me I should be able to do something like this:
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;
}
}
However, as commented in the code above, the fields are not recognized as existing on the class within the aspect. What am I doing wrong?
The error I get is: transferCount cannot be resolved or is not a field
回答1:
Something is happening in the Account
class which unfortunately you didn't share here. Please learn what an MCVE is and why it is so valuable to always provide one. Especially in the context of AOP it is even more important because an aspect does not make much sense without a target class. I cannot debug one without the other, which is why I had to invent my own dummy class. That would actually have been your job.
Probably you are trying to use the declared private members directly from within the Account
class. For a reason I do not understand yet, this does not work because it throws off the AspectJ compiler with a The method getTransferCount() from the type Account is not visible
or similar error message. This must be a limitation or a bug in AspectJ, I will ask the maintainer and report back here later.
But first let us reproduce your situation:
Application 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());
}
}
Aspect:
First let me mention that I fixed two errors in your code:
Your pointcut will only match if you bind the arguments correctly.
double amount
is the second of two method parameters, not the only one. Thus you have to writeargs(*, amount)
instead ofargs(amount)
You increment
transferCount
before checking fors.getTransferCount() == 0
, so theif
condition will never match. What you want iss.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;
}
}
Now in Eclipse I see the compile error in the application class and due to the failed compilation the subsequent problem in the aspect itself. As soon as you comment out the last line of the main
method, it works. (Maybe you have to re-save the aspect or recompile the project in order to make the squiggly lines disappear.)
Actually the easiest thing to do is to make getTransferCount()
public instead of private. Getters are usually public and you then can also use the method from the main
method again and the program output would become:
50% markup
2
BTW, inside the aspect you do not need to use getTransferCount()
. Just like in the line above, you can directly access the field.
Update: I promised you an answer to the question why the target class cannot access fields and methods declared as private
via ITD: because they are private with respect to the aspect itself! This answer comes from the AspectJ maintainer himself, please read the full answer here.
来源:https://stackoverflow.com/questions/61429008/aspectj-inter-type-field-not-recognized-in-advice