AspectJ 指示符 @args() 在 Spring AOP 中不起作用

AspectJ designator @args() not working in Spring AOP

我正在学习 Spring 并且我搜索了很多有关如何正确使用 @args() AspectJ 指示符的信息,但我仍然不完全清楚。我所知道的是,它将连接点匹配限制为执行其参数使用给定注释类型进行注释的方法。这对我来说似乎不起作用。

所以这是我的文件:

Human.java

@Component
public class Human {
    int sleepHours;
    public int sleep(String sleepHours) {
        this.sleepHours = Integer.parseInt(sleepHours);
        System.out.println("Humans sleep for " + this.sleepHours + " hours.");
        return this.sleepHours+1;
    }
}

Sleepable.java - 可睡眠注释[​​=15=]

 package com.aspect;
 public @interface Sleepable {

 }

SleepingAspect.java - 看点

@Component
@Aspect
public class SleepingAspect {

    @Pointcut("@args(com.aspect.Sleepable)")
    public void sleep(){};

    @Before("sleep()")
    public void beforeSleep() {
        System.out.println("Closing eyes before sleeping");
    }

    @AfterReturning("sleep()")
    public void afterSleep() {
        System.out.println("Opening eyes after sleep");
    }
}

MainApp.java

public class MainApp {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        Human human = (Human) context.getBean("human");
        @Sleepable
        String sleepHours = "8";
        human.sleep(sleepHours);
    }
}

输出

人的睡眠时间为 8 小时。

预期输出

睡前闭眼

人的睡眠时间为 8 小时。

睡后睁眼

你的代码有几个错误:

  • Spring AOP 只能拦截对 Spring 组件的方法调用。您要拦截的是局部变量上的注释。甚至更强大的 AspectJ 也无法拦截任何有关局部变量的内容,只能拦截 read/write 对 class 成员的访问。因此,您尝试做的事情是不可能的。顺便说一下,这是糟糕的应用程序设计。为什么有人在尝试应用横切行为时想要依赖方法内部?方法内部结构需要经常重构。建议:将注释放在方法 public int sleep(String sleepHours).
  • 您的注释在运行时是不可见的,因为您忘记添加像 @Retention(RetentionPolicy.RUNTIME).
  • 这样的元注释
  • @args 是错误的切入点类型。它捕获其类型被注释的方法参数。您想改用 @annotation(com.aspect.Sleepable)

我认为您不应该尝试使用 Spring AOP 进行复制和粘贴冷启动,而应该先阅读 Spring AOP manual。我在这里解释的所有内容都可以在那里找到。


更新: 所以根据你的评论,你只是在练习并试图为 @args() 编一个例子。这是普通 AspectJ 中的一个。您可以在 Spring AOP 中以类似的形式轻松使用它。 @Before 建议向您展示了如何匹配参数及其 class 上的注释,@After 建议还显示了如何将相应的注释绑定到建议参数。

注解+class使用:

package com.company.app;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {}
package com.company.app;

@MyAnnotation
public class MyClass {}

驱动申请:

package com.company.app;

public class Application {
  public static void main(String[] args) {
    new Application().doSomething(new MyClass(), 11);
  }

  public String doSomething(MyClass myClass, int i) {
    return "blah";
  }
}

如您所见,我们在方法参数中使用了带注释的 class MyClass。这可以在以下方面与@args()匹配。

看点:

package com.company.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

import com.company.app.MyAnnotation;

@Aspect
public class MyAspect {
  @Before("@args(com.company.app.MyAnnotation, ..)")
  public void myBeforeAdvice(JoinPoint thisJoinPoint) {
    System.out.println("Before " + thisJoinPoint);
  }

  @After("@args(myAnnotation, ..)")
  public void myAfterAdvice(JoinPoint thisJoinPoint, MyAnnotation myAnnotation) {
    System.out.println("After " + thisJoinPoint + " -> " + myAnnotation);
  }
}

控制台日志:

Before call(String com.company.app.Application.doSomething(MyClass, int))
Before execution(String com.company.app.Application.doSomething(MyClass, int))
After execution(String com.company.app.Application.doSomething(MyClass, int)) -> @com.company.app.MyAnnotation()
After call(String com.company.app.Application.doSomething(MyClass, int)) -> @com.company.app.MyAnnotation()

当然,call()连接点在Spring AOP中不可用,所以你只会看到两行日志输出。