我是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
首先,我建议阅读AspectJ文档来学习语法。当您使用本机AspectJ语法时,这就像学习一种新的编程语言,或者至少学习一个Java扩展。您正在做的是将本机语法与基于注释的语法混合。试着坚持一个。我确信您在任何教程中都没有发现这一点,但最终通过反复尝试获得了该语法。
您不需要以本机语法绑定joinpoint参数,因为它是隐式且自动存在的。自动绑定的连接点总是命名为thisJoinPoint
,所有教程都会向您展示这一点。只有在基于注释的语法中,您才需要绑定连接点,并可以根据自己的意愿对其进行命名,但即使这样,我也建议您坚持使用thisJoinPoint
,因为从注释到本机语法的重构更容易,而且您的眼睛已经习惯于在方面代码中发现变量名称。
您得到的警告意味着您定义的切入点与代码的任何部分都不匹配,至少与方面编织器或编译器可见的任何部分不匹配。发生这种情况的原因可能有很多,例如拼写错误的包或类名,建议返回类型错误(对于非void方法,返回类型必须为Object
,或者更具体地说,与要拦截的方法返回的内容匹配(。假设例如checkUser(..)
返回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")));
}
}
正如您所看到的,由于我为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
类,所以这里不需要使用完全限定的类名。同样,这是本机语法的优势,在基于注释的语法中,您必须使用完全限定的名称。
我还用更明确的boolean MyUI.checkUser(*)
替换了通用的* MyUI.checkUser(..)
(它也会起作用(,因为我们已经知道该方法返回了一个布尔值,并且只有一个参数,无论如何,我们都会通过从around建议中返回布尔值和通过args()
绑定一个参数来假设这一点。您也可以更具体地使用boolean MyUI.checkUser(User)
。
此外,我使用execution()
而不是call()
,因为它更高效,因为它只将建议代码编织到执行方法中一次,而不是为主方法中的每个方法调用编织五次。如果MyUI
类在AspectJ weaver/编译器无法访问的情况下,即因为它不在使用AspectJ Maven编译的模块中,则只需要使用call()
。
控制台日志:
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"。