AspectJ 麻烦使用周围建议和 ProceedingJoinPoint

AspectJ trouble using around advice and ProceedingJoinPoint

我是 AOP 新手,我需要在我的项目中使用 AspectJ。 我需要使用 around advice 但我在使用它时遇到问题,我的 .aj class,

中有以下代码
pointcut checkUser(ProceedingJoinPoint jp,User user): call(* com.example.UserAccount.MyUI.checkUser(..))&& args(jp,user);

void around(ProceedingJoinPoint jp,User user) throws Throwable : checkUser(jp,user){
    // Condition checks one of the user boolean property
    if(condition){
        jp.proceed();
    }else{
        // Do nothing
    }   
}

但我一直收到此警告,

advice defined in Aspects.UserAccount has not been applied [Xlint:adviceDidNotMatch]

顺便说一句,我在没有 ProceedingJoinPoint 的情况下尝试过,只尝试了 proceed();,但随后收到了这个警告,too few arguments to proceed, expected 1

非常感谢您提供的任何帮助或提示!

礼萨

首先,我建议阅读 AspectJ 文档以学习语法。当您使用本机 AspectJ 语法时,这就像学习一种新的编程语言或至少是 Java 扩展。您正在做的是将本机语法与基于注释的语法混合在一起。尝试坚持一个。我相信您在任何教程中都没有找到这个,但通过反复试验最终得到了那个语法。

您不需要在本机语法中绑定连接点参数,因为它会自动隐式存在。自动绑定的连接点总是命名为 thisJoinPoint,所有教程都会向您展示。只有在基于注释的语法中,您才需要绑定连接点并可以根据需要命名它,但即便如此,我还是建议坚持使用 thisJoinPoint,因为这样从注释到本机语法的重构会更容易,并且您的眼睛会习惯于发现方面代码中的那个变量名。

您收到的警告意味着您定义的切入点不匹配您的代码的任何部分,至少不匹配切面编织器或编译器可见的任何部分。发生这种情况的原因可能有很多,例如包或 class 名称拼写错误,建议 return 类型错误(return 类型必须是 Object 对于非 void 方法或更具体地匹配您要拦截的方法 returns)。假设例如checkUser(..) returns a boolean,around advice 应该做同样的事情。我使用您的包和 class 名称制作了一个示例。此外,包名称应该是小写的,但我使用了你的,假设它们真的是包名称而不是内部 classes:

帮手class:

package com.example.UserAccount;

public class User {
  private String name;

  public User(String name) {
    this.name = name;
  }

  public String getName() {
    return name;
  }

  @Override
  public String toString() {
    return "User(" + name + ")";
  }
}

Class 方面+示例主要方法的目标:

package com.example.UserAccount;

public class MyUI {
  public boolean checkUser(User user) {
    return user.getName().toUpperCase().contains("ADMIN");
  }

  public static void main(String[] args) {
    MyUI ui = new MyUI();
    System.out.println(ui.checkUser(new User("Administrator")));
    System.out.println(ui.checkUser(new User("john")));
    System.out.println(ui.checkUser(new User("xander")));
    System.out.println(ui.checkUser(new User("admiral")));
    System.out.println(ui.checkUser(new User("SySaDmiN")));
  }
}

如您所见,我们期望第一个和最后一个条目的输出为 "true",但由于我为 [=17 弥补的检查逻辑,中间的输出为 "false" =].

现在让我们为名为 "Xander" 的用户编写一个 returns "true" 方面,例如为了给他管理员权限或其他什么。我编造这个是因为你没有像在 Whosebug 上那样提供一个 MCVE,而只是一个不连贯的代码片段,它让每个人都试图回答你的问题,猜测你到底想实现什么以及如何重现你的问题。

看点:

package Aspects;

import com.example.UserAccount.User;
import com.example.UserAccount.MyUI;

public aspect UserAccount {
  pointcut checkUser(User user) :
    execution(boolean MyUI.checkUser(*)) && args(user);

  boolean around(User user) : checkUser(user) {
    System.out.println(thisJoinPoint + " -> " + user);
    if (user.getName().equalsIgnoreCase("xander"))
      return true;
    return proceed(user);
  }
}

我刚刚导入了 MyUI class,所以这里不需要使用完全限定的 class 名称。同样,这是本机语法的优势,在基于注释的语法中,您必须使用完全限定名称。

我还用更明确的 boolean MyUI.checkUser(*) 替换了通用的 * MyUI.checkUser(..) (这也可以),因为我们已经知道方法 return 是一个布尔值并且只有一个参数,无论如何,我们都通过 return 从 around advice 中输入一个布尔值并通过 args() 绑定一个参数来假设这一点。您也可以更具体地使用 boolean MyUI.checkUser(User).

此外,我使用 execution() 而不是 call() 因为它更有效,因为它将建议代码编织到执行方法一次而不是每次方法调用中的五次主要方法。如果 MyUI class 超出 AspectJ weaver/compiler 的范围,您只需要使用 call(),即因为它不在您使用 AspectJ Maven 编译的模块中。

控制台日志:

execution(boolean com.example.UserAccount.MyUI.checkUser(User)) -> User(Administrator)
true
execution(boolean com.example.UserAccount.MyUI.checkUser(User)) -> User(john)
false
execution(boolean com.example.UserAccount.MyUI.checkUser(User)) -> User(xander)
true
execution(boolean com.example.UserAccount.MyUI.checkUser(User)) -> User(admiral)
false
execution(boolean com.example.UserAccount.MyUI.checkUser(User)) -> User(SySaDmiN)
true

Et voilà,这方面有效。它使用户 "xander".

的目标方法 return "true"