aspectjweaver给我提供了“应用于构造函数执行的不兼容返回类型”

4dc9hkyq  于 2021-07-09  发布在  Java
关注(0)|答案(1)|浏览(375)

我正在尝试使用@around advice将用于跟踪的上下文注入到新创建的可调用对象中:

@Aspect
@Configurable
public class TracingAspect {

    @Around("execution(java.util.concurrent.Callable+.new(..))")
    public Callable wrapExecutor(ProceedingJoinPoint pjp) throws Throwable {
        Context context = Context.current();
        return context.wrap((Callable) pjp.proceed());
    }
}

当weaver遇到预期的连接点时,如以下示例中的匿名可调用实现:

public class Foo {

    private ExecutorService threadpool = Executors.newFixedThreadPool(10);

    public Future<String> doStuffAsync() throws InterruptedException {

        return threadpool.submit(new Callable<Integer>() {

            @Override
            public Integer call() throws Exception {
                System.out.println(42);
                return 42;
            }
        });
    }
}

我从aspectj weaver收到以下错误消息: error at foo/bar/tracing/aspect/TracingAspect.java::0 incompatible return type applying to constructor-execution(void foo.bar.Foo$1.<init>(foo.bar.Foo)) 我做错什么了?

klr1opcd

klr1opcd1#

首先,不能替换构造函数中返回的对象 execution() ,因为构造函数不是一个普通的方法,例如可以将父类型或接口作为返回类型。构造函数总是准确地返回所定义类型的对象,而不是其他类型。这甚至不是aspectj限制,而是jvm限制。使用诸如asm之类的字节码工程库,您将有相同的限制。
因此,在aspectj中,最好的方法是替换构造函数中返回的对象 call() ,但对象也必须与预期类型匹配。不幸的是,opentelemetry返回一个lambda示例,该示例不能转换为完全匿名的 Callable 代码中的子类型。这意味着,在这种代码结构下,您无法执行任何操作。
作为一种解决方法,您可以拦截对方法的调用 Callable 示例,例如 ExecutorService.submit(Callable) . 你只需要确保捕捉到所有相关的。例如:

package de.scrum_master.app;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.Callable;

public class Application {
  private ExecutorService threadpool = Executors.newFixedThreadPool(10);

  public Future<Integer> doStuffAsync() throws InterruptedException {
    return threadpool.submit(new Callable<Integer>() {
      @Override
      public Integer call() throws Exception {
        return 42;
      }
    });
  }

  public Future<Integer> doStuffLambdaAsync() throws InterruptedException {
    return threadpool.submit(() -> 77);
  }

  public static void main(String[] args) throws Exception {
    Application app = new Application();
    System.out.println("Future value = " + app.doStuffAsync().get());
    System.out.println("Future value (lambda) = " + app.doStuffLambdaAsync().get());
  }
}
package de.scrum_master.aspect;

import java.util.concurrent.Callable;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import io.opentelemetry.context.Context;

@Aspect
public class TracingAspect {
  @Around("call(* java.util.concurrent.ExecutorService.submit(*)) && args(callable)")
  public Object wrapExecutor(ProceedingJoinPoint pjp, Callable<?> callable) throws Throwable {
    System.out.println(pjp);
    Context context = Context.current();
    return pjp.proceed(new Object[] { context.wrap(callable) });
  }
}
call(Future java.util.concurrent.ExecutorService.submit(Callable))
Future value = 42
call(Future java.util.concurrent.ExecutorService.submit(Callable))
Future value (lambda) = 77

当然,如果您能够准确地确定那些属性(例如,从哪个类、哪个包中进行调用等),那么您也可以对拦截的调用进行筛选,以获得具有某些属性的可调用项。

相关问题