Aspectj:lambda表达式的切入点 [英] Aspectj: Pointcut on lambda expression

查看:172
本文介绍了Aspectj:lambda表达式的切入点的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a Java6 project that is being migrated to Java8. We used aspectj to log some of users actions, like button clicking.

所以有这样的听众:

    button.addClickListener(new Button.ClickListener() {
        @Override
        public void buttonClick(Button.ClickEvent clickEvent) {
            doSth();
        }
    });

和poincut:

@Pointcut("execution(public void Button.ClickListener.buttonClick(Button.ClickEvent))")
public void buttonClick() {};

但由于我们将使用Java8,听众将是这样的:

But since we will use Java8, listeners will be like this:

button.addClickListener(clickEvent -> doSth());

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

Is there any way to write aspectj pointcut, so that it handles new listeners?

推荐答案

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

I guess the problem is that lambdas do not seem to actually implement/override any interface methods with a corresponding name, but create an anonymous method. Look at this example:

虚拟按钮类,复制我们需要的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);
    }
}

驱动程序应用程序:

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

如您所见,创建了两个按钮实例,一个带有经典的匿名类监听器,一个带有lambda监听器。两个按钮都被点击,因此控制台日志如下所示:

As you can see, two button instances are created, one with a classical anonymous class listener, one with a lambda listener. Both buttons get clicked, so consequently the console log looks like this:

Clicking button
Clicking button

看点:

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

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

The first advice uses a pointcut similar to yours. It can only intercept classical listener declarations.

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

The second advice is for debugging purposes and logs all joinpoints within the driver application in order to show what the heck is going on here.

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

Last, but not least, the third advice shows a workaround for intercepting lambda-based listeners, relying on the knowledge about Java compiler naming for lambdas acquired from the debug output. This is not very nice, but for the moment it works. Please note that the lambda version of buttonClick(..) is not public, so it is just void *..lambda*, not 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())






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


Update: There is a corresponding Bugzilla issue now for AspectJ. I have just created it. It also points to a recent discussion on the mailing list.

这篇关于Aspectj:lambda表达式的切入点的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆