Aspectj:lambda表达式的切入点

Let*_*ete 7 lambda aspectj java-8

我有一个正在迁移到Java8的Java6项目.我们使用aspectj来记录一些用户操作,例如按钮点击.

所以有听众这样:

    button.addClickListener(new Button.ClickListener() {
        @Override
        public void buttonClick(Button.ClickEvent clickEvent) {
            doSth();
        }
    });
Run Code Online (Sandbox Code Playgroud)

和poincut:

@Pointcut("execution(public void Button.ClickListener.buttonClick(Button.ClickEvent))")
public void buttonClick() {};
Run Code Online (Sandbox Code Playgroud)

但是既然我们将使用Java8,那么听众将是这样的:

button.addClickListener(clickEvent -> doSth());
Run Code Online (Sandbox Code Playgroud)

有没有办法编写aspectj切入点,以便它处理新的侦听器?

kri*_*aex 4

我猜问题是 lambda 似乎并没有实际实现/覆盖任何具有相应名称的接口方法,而是创建一个匿名方法。看这个例子:

虚拟按钮类,复制我们在这里需要的 Vaadin 部分:

package de.scrum_master.app;

public class Button {
    private ClickListener listener;

    public void addClickListener(ClickListener listener) {
        this.listener = listener;
    }

    public void click() {
        System.out.println("Clicking button");
        listener.buttonClick(new ClickEvent());
    }

    public static class ClickEvent {}

    public static interface ClickListener {
        public void buttonClick(ClickEvent clickEvent);
    }
}
Run Code Online (Sandbox Code Playgroud)

驱动程序应用:

package de.scrum_master.app;

public class Application {
    protected static void doSomething() {}

    public static void main(String[] args) {
        Button button = new Button();
        button.addClickListener(new Button.ClickListener() {
            @Override
            public void buttonClick(Button.ClickEvent clickEvent) {
                doSomething();
            }
        });
        button.click();

        button = new Button();
        button.addClickListener(clickEvent -> doSomething());
        button.click();
    }
}
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,创建了两个按钮实例,一个具有经典的匿名类侦听器,一个具有 lambda 侦听器。这两个按钮都被单击,因此控制台日志如下所示:

Clicking button
Clicking button
Run Code Online (Sandbox Code Playgroud)

方面:

package de.scrum_master.aspect;

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

@Aspect
public class ButtonClickLogger {
    @Before("execution(public void *..Button.ClickListener.buttonClick(*..Button.ClickEvent))")
    public void logButtonClick(JoinPoint thisJoinPoint) {
        System.out.println("Caught button click: " + thisJoinPoint);
    }

    @Before("within(*..Application)")
    public void logAllInListener(JoinPoint thisJoinPoint) {
        System.out.println("  " + thisJoinPoint);
    }

    @Before("execution(void *..lambda*(*..Button.ClickEvent))")
    public void logButtonClickLambda(JoinPoint thisJoinPoint) {
        System.out.println("Caught button click (lambda): " + thisJoinPoint);
    }
}
Run Code Online (Sandbox Code Playgroud)

第一个建议使用与您类似的切入点。它只能拦截经典的侦听器声明。

第二个建议用于调试目的,并记录驱动程序应用程序中的所有连接点,以显示这里到底发生了什么。

最后但并非最不重要的一点是,第三个建议展示了一种拦截基于 lambda 的侦听器的解决方法,它依赖于从调试输出获取的 lambda 的 Java 编译器命名知识。这不是很好,但目前它可以工作。请注意, 的 lambda 版本buttonClick(..)不是公开的,因此它只是void *..lambda*, 不是public void *..lambda*

使用 AspectJ 的控制台输出:

  staticinitialization(de.scrum_master.app.Application.<clinit>)
  execution(void de.scrum_master.app.Application.main(String[]))
  call(de.scrum_master.app.Button())
  call(de.scrum_master.app.Application.1())
  staticinitialization(de.scrum_master.app.Application.1.<clinit>)
  preinitialization(de.scrum_master.app.Application.1())
  initialization(de.scrum_master.app.Application.1())
  initialization(de.scrum_master.app.Button.ClickListener())
  execution(de.scrum_master.app.Application.1())
  call(void de.scrum_master.app.Button.addClickListener(Button.ClickListener))
  call(void de.scrum_master.app.Button.click())
Clicking button
Caught button click: execution(void de.scrum_master.app.Application.1.buttonClick(Button.ClickEvent))
  execution(void de.scrum_master.app.Application.1.buttonClick(Button.ClickEvent))
  call(void de.scrum_master.app.Application.doSomething())
  execution(void de.scrum_master.app.Application.doSomething())
  call(de.scrum_master.app.Button())
  call(void de.scrum_master.app.Button.addClickListener(Button.ClickListener))
  call(void de.scrum_master.app.Button.click())
Clicking button
  execution(void de.scrum_master.app.Application.lambda$0(Button.ClickEvent))
Caught button click (lambda): execution(void de.scrum_master.app.Application.lambda$0(Button.ClickEvent))
  call(void de.scrum_master.app.Application.doSomething())
  execution(void de.scrum_master.app.Application.doSomething())
Run Code Online (Sandbox Code Playgroud)

更新: AspectJ 现在有相应的Bugzilla 问题。我刚刚创建了它。它还指出了邮件列表上最近的讨论。