对通过lambda表达式调用的Runnable.run方法执行@Before和@After方面

时间:2018-04-03 04:37:38

标签: java spring lambda aop

描述 如何为Runnable.run方法创建切入点以便可以在java 8 lambda表达式中调用@Before和@After方面。

  1. 为Runnable.run方法创建切入点
  2. 在步骤1中为切入点创建@Before方面。 --->在runnbale之前打印
  3. 在步骤1中为切入点创建@Aefore方面。 --->打印后可运行
  4. 调用以下行时

    executor.runAsync(() ->
    
    { System.out.println("Runnable invoked"); }
    )
    

    预期输出:

    Before runnable
    Runnable invoked
    After runnable
    

    来自@AspectJ. Pointcut for scala (and probably java) lambdas的解决方案无法解决此问题。

    @Around(" execution(void com.test..lambda *(..)) 这适用于所有lambda表达式......我想仅限于Runnable.run方法。

1 个答案:

答案 0 :(得分:1)

你不能因为执行的lambda方法是静态的,即你甚至无法检查像thisJoinPoint.getTarget() instanceof Runnable这样的东西,因为目标是null。使用匿名子类,这将有效。

即。在关于AspectJ Bugzilla issue的事情已经完成之前,你无法真正解决这个问题。

更新:我找到了一个解决方法。这不好,但至少它可以工作,直到AspectJ更好地支持lambdas。你需要将它定制为调用有问题的runnable的方法,但是:

驱动程序应用程序:

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

<强>方面:

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

控制台日志:

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