问题
Description How to create a pointcut for Runnable.run method So that @Before and @After aspect can be invoked in java 8 lambda expression.
- Create pointcut for Runnable.run method
- Create @Before aspect for pointcut in step 1 . ---> Print Before runnbale
- Create @Aefore aspect for pointcut in step 1 . ---> Print After runnable
When below line invoked
executor.runAsync(() ->
{ System.out.println("Runnable invoked"); }
)
Output expected :
Before runnable
Runnable invoked
After runnable
Solution from @AspectJ. Pointcut for scala (and probably java) lambdas not worked in this problem.
@Around("execution(void com.test..lambda*(..)) This will work for all lambda expression ... And I want to restrict this for Runnable.run method only.
回答1:
You cannot because the executed lambda methods are static, i.e. you cannot even check something like thisJoinPoint.getTarget() instanceof Runnable
because the target is null. With anonymous subclasses this would work.
I.e. before something has been done about my AspectJ Bugzilla issue, you cannot really solve this problem.
Update: I found a workaround for you. It is not nice, but at least it works until AspectJ supports lambdas better. You need to tailor it to the method calling the runnable in question, though:
Driver application:
package de.scrum_master.app;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
public class Application {
public static void main(String[] args) throws InterruptedException, ExecutionException {
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
try {
MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
System.out.println("Separate thread lambda");
});
CompletableFuture<Void> future2 = CompletableFuture.runAsync(new Runnable() {
@Override
public void run() {
try {
MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
System.out.println("Separate thread anonymous Runnable");
}
});
System.out.println("Main thread");
future.get();
future2.get();
}
}
Aspect:
package de.scrum_master.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 org.aspectj.lang.annotation.Pointcut;
@Aspect
public class AsyncRunInterceptor {
private static final String CLASS_ASYNC_RUN = "java.util.concurrent.CompletableFuture$AsyncRun";
private static final String METHOD_RUN = "run";
@Pointcut("execution(void *(..)) && !within(de.scrum_master.aspect.AsyncRunInterceptor) && if()")
public static boolean isAsyncRun() {
final StackTraceElement[] stackTrace = new Exception().getStackTrace();
if (stackTrace.length < 3)
return false;
final StackTraceElement stackTraceElement = stackTrace[2];
return
stackTraceElement.getClassName() == CLASS_ASYNC_RUN
&& stackTraceElement.getMethodName() == METHOD_RUN;
}
@Before("isAsyncRun()")
public void beforeAsyncRun(JoinPoint thisJoinPoint) {
System.out.println("[" + Thread.currentThread().getId() + "] BEFORE " + thisJoinPoint);
}
@After("isAsyncRun()")
public void afterAsyncRun(JoinPoint thisJoinPoint) {
System.out.println("[" + Thread.currentThread().getId() + "] AFTER " + thisJoinPoint);
}
}
Console log:
Main thread
[10] BEFORE execution(void de.scrum_master.app.Application.lambda$0())
[11] BEFORE execution(void de.scrum_master.app.Application.1.run())
Separate thread lambda
Separate thread anonymous Runnable
[11] AFTER execution(void de.scrum_master.app.Application.1.run())
[10] AFTER execution(void de.scrum_master.app.Application.lambda$0())
来源:https://stackoverflow.com/questions/49622347/execute-before-and-after-aspect-for-runnable-run-method-which-invoked-through