AspectJ-获取使用AspectJ生成lambda函数的父线程的线程ID

问题描述 投票:0回答:1

我有以下代码:

@RequestMapping(method = RequestMethod.GET, path = "/execute")
public @ResponseBody
String execute(@RequestParam("name") String input) throws Exception {

    ExecutorService executor = Executors.newSingleThreadExecutor();
    executor.execute(() -> {
        try {
            // Do something...
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    });
    return "lambda call";
}

我想使用aspectJ来捕获lambda函数的执行,并标识生成该lambda函数的线程的ID,即我的“ execute”函数在其中运行的线程。我知道如何捕获lambda函数-

execution(void lambda$*(..)

但是对于我来说,识别创建该线程的线程ID(称为“执行”的线程ID)为时已晚,因为lambda在新线程中运行。如何获得“父”线程ID /“执行”线程ID?

java multithreading aspectj
1个回答
0
投票

您在这里有几个问题:

  • AspectJ当前无法通过execution()切入点编织到lambda中。这主要是由于AspectJ编译器/编织器忽略了JVM指令invokedynamic。另请参见AspectJ票证#471347 (created by myself)#364886。此外,如果您改用匿名Runnable类,则可以轻松拦截它。

  • 您不是自己创建和启动线程,而是将其推迟到JDK类和方法,例如ExecutorService.execute(Runnable),即,您也不能编织到它们的execution()中,而只能编织到您自己创建的call()中(方面-编织)代码。

  • 在Java中,没有诸如“父线程”之类的通用概念,您可以通过虚拟方法(如Thread.getParent()或类似方法)从执行线程中轻松确定。有一些为线程组实现的父级东西,但这对您没有帮助。

所以剩下的就是这样的间接方式:

驱动程序应用程序:

package de.scrum_master.app;

import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Application {
  String execute(String input) throws Exception {
    ExecutorService executor = Executors.newSingleThreadExecutor();

    executor.execute(() -> {
      try {
        doSomething();
      } catch (IOException e) {
        e.printStackTrace();
        throw new RuntimeException(e);
      }
    });

    return "lambda call";
  }

  private void doSomething() throws IOException {}

  public static void main(String[] args) throws Exception {
    new Application().execute("dummy");
  }
}

方面:

package de.scrum_master.aspect;

import java.util.concurrent.ExecutorService;

public aspect MyAspect {
  // Catch-all advice for logging purposes
  before() : !within(MyAspect) {
    System.out.println("  " + thisJoinPoint);
  }

  // Intercept calls to ExecutorService.execute(*)
  before(Runnable runnable) : call(void ExecutorService.execute(*)) && args(runnable) {
    System.out.println(Thread.currentThread() + " | " + thisJoinPoint + " -> " + runnable);
  }

  // Intercept lambda executions
  before() : execution(private void lambda$*(..)) {
    System.out.println(Thread.currentThread() + " | " + thisJoinPoint);
  }
}

控制台日志:

  staticinitialization(de.scrum_master.app.Application.<clinit>)
  execution(void de.scrum_master.app.Application.main(String[]))
  call(de.scrum_master.app.Application())
  preinitialization(de.scrum_master.app.Application())
  initialization(de.scrum_master.app.Application())
  execution(de.scrum_master.app.Application())
  call(String de.scrum_master.app.Application.execute(String))
  execution(String de.scrum_master.app.Application.execute(String))
  call(ExecutorService java.util.concurrent.Executors.newSingleThreadExecutor())
  call(void java.util.concurrent.ExecutorService.execute(Runnable))
Thread[main,5,main] | call(void java.util.concurrent.ExecutorService.execute(Runnable)) -> de.scrum_master.app.Application$$Lambda$1/2046562095@2dda6444
  execution(void de.scrum_master.app.Application.lambda$0())
Thread[pool-1-thread-1,5,main] | execution(void de.scrum_master.app.Application.lambda$0())
  call(void de.scrum_master.app.Application.doSomething())
  execution(void de.scrum_master.app.Application.doSomething())
© www.soinside.com 2019 - 2024. All rights reserved.