首页 > 解决方案 > AspectJ 在使用周围建议和 ProceedingJoinPoint 时遇到问题

问题描述

我是 AOP 的新手,我需要在我的项目中使用 AspectJ。我需要使用周围的建议,但我在使用它时遇到问题,我的 .aj 类中有以下代码,

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

我很感谢任何单一的帮助或提示!

礼萨

标签: javaaopaspectjaspectj-maven-plugin

解决方案


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

您不需要在本机语法中绑定连接点参数,因为它隐式且自动地存在。自动绑定的连接点总是被命名thisJoinPoint为所有教程肯定会告诉你的。只有在基于注解的语法中,您才需要绑定连接点并可以随意命名,但即便如此,我还是建议坚持使用,thisJoinPoint因为从注解重构为本机语法更容易,而且您的眼睛习惯于在您的方面代码。

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

助手类:

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 + ")";
  }
}

方面 + 示例主要方法针对的类:

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”,但由于我弥补的检查逻辑,我们希望在两者之间输出“false” checkUser(..)

现在让我们编写一个方面,它也为名为“Xander”的用户返回“true”,例如为了给他管理员权限或其他什么。我之所以编造这个,是因为您没有像在 StackOverflow 上一直提供的那样提供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类,所以这里不需要使用完全限定的类名。同样,这是本机语法的一个优势,在基于注释的语法中,您必须使用完全限定名称。

我还用更明确的方法替换了泛型* MyUI.checkUser(..)(它也可以工作),boolean MyUI.checkUser(*)因为我们已经知道该方法返回一个布尔值并且只有一个参数,无论如何我们都假设通过从周围建议返回一个布尔值并绑定一个参数通过args(). 您还可以更具体并使用boolean MyUI.checkUser(User).

此外,我使用execution()而不是call()因为它更有效,因为它将建议代码编织到执行方法中一次,而不是在主方法中的每个方法调用中编织五次。您只需要在 AspectJ 编织器/编译器无法访问该类时使用,call()MyUI因为它不在您使用 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

等等,这方面有效。它使目标方法为用户“xander”返回“true”。


推荐阅读