Aspectj: Pointcut for lambda expression

I have a Java6 project that is porting to Java8. We used aspectj to register some user actions, such as clicking a button.

So, there are such listeners:

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

And poincut:

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

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

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

Is there a way to write a pointcut pointcut so that it handles new listeners?

+6
source share
1 answer

I think the problem is that lambdas does not seem to implement / override any interface methods with the corresponding name, but create an anonymous method. Take a look at this example:

The "empty" class, replicating parts of Vaadin, we need here:

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

Driver Application:

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

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

 Clicking button Clicking button 

Format:

 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 tip uses a pointcut similar to yours. It can only intercept ads of the classical listener.

The second tip is for debugging purposes and registers all connection points in the driver application to show what is happening here.

Last, but not least, the third tip shows a workaround for intercepting lambda-based listeners, based on knowledge of the purpose of the Java compiler for lambdas derived from debug output. This is not very nice, but at the moment it works. Note that the lambda version of buttonClick(..) is not publicly available, so it’s just void *..lambda* , not public void *..lambda* .

Console output with 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()) 

Update: There is currently a related bugzilla issue for AspectJ. I just created it. He also points to a recent discussion on the mailing list.

+2
source

Source: https://habr.com/ru/post/988908/


All Articles