AspectJ在使用周围建议和ProceedingJoinPoint时遇到麻烦

时间:2018-11-22 15:03:45

标签: java aop aspectj aspectj-maven-plugin

我是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的警告

感谢任何帮助或提示!

Reza

1 个答案:

答案 0 :(得分:1)

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

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

所收到的警告意味着您定义的切入点与代码的任何部分都不匹配,至少与weaver或编译器可见的任何部分都不匹配。发生这种情况的原因可能很多,例如包或类名拼写错误,建议返回类型错误(非无效方法的返回类型必须为Object,或更具体地,要与要拦截的方法匹配的返回类型)。假设例如checkUser(..)返回一个boolean,周围的建议也应这样做。我用您的包和类名组成了一个示例。此外,程序包名称应为小写字母,但我使用的是您的,假设它们确实是程序包名称而不是内部类:

助手类:

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

如您所见,由于我为checkUser(..)编写了校验逻辑,因此我们期望第一个条目和最后一个条目的输出为“ true”,而介于两者之间的输出为“ false”。

现在,让我们编写一个方面,该方面也会为名为“ 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()是因为它效率更高,因为它将建议代码仅编织到执行方法一次,而不是将main方法中的每个方法调用编织五次。仅当call()类超出AspectJ编织者/编译器的范围时,才需要使用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”。