Java AspectJ weaver给我的是;应用于构造函数执行的不兼容返回类型“;对构造函数使用@Around建议时

Java AspectJ weaver给我的是;应用于构造函数执行的不兼容返回类型“;对构造函数使用@Around建议时,java,aop,aspectj,Java,Aop,Aspectj,我正在尝试使用@Around advice将跟踪上下文注入新创建的可调用对象: @Aspect @Configurable public class TracingAspect { @Around("execution(java.util.concurrent.Callable+.new(..))") public Callable wrapExecutor(ProceedingJoinPoint pjp) throws Throwable {

我正在尝试使用@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;
            }
        });
    }
}
公共类Foo{
私有ExecutorService线程池=Executors.newFixedThreadPool(10);
public Future doStuffAsync()引发InterruptedException{
返回threadpool.submit(新的可调用(){
@凌驾
公共整数调用()引发异常{
系统输出println(42);
返回42;
}
});
}
}
我从Aspectj weaver收到以下错误消息:

应用于构造函数执行的foo/bar/tracing/aspect/TracingAspect.java::0返回类型不兼容(void foo.bar.foo$1.(foo.bar.foo))


我在这里做错了什么?

首先,您不能从构造函数
execution()
中替换返回的对象,因为构造函数不是一个正常的方法,例如,可以将超类型或接口作为返回类型。构造函数总是准确地返回定义类型的对象,而不返回其他对象。这甚至不是AspectJ限制,而是JVM限制。使用诸如ASM之类的字节码工程库,您将有相同的限制

因此,在AspectJ中可以做的最好的事情就是替换构造函数
call()
中返回的对象,但该对象也必须与预期的类型匹配。不幸的是,OpenTelemetry返回一个lambda实例,该实例无法转换为代码中的确切的匿名
Callable
子类型。这意味着,对于这种代码结构,您在这里无能为力

作为一种解决方法,您可以拦截对采用
Callable
实例的方法的调用,例如
ExecutorService.submit(Callable)
。您只需要确保捕获所有相关的。例如:

package de.scrum\u master.app;
导入java.util.concurrent.ExecutorService;
导入java.util.concurrent.Executors;
导入java.util.concurrent.Future;
导入java.util.concurrent.Callable;
公共类应用程序{
私有ExecutorService线程池=Executors.newFixedThreadPool(10);
public Future doStuffAsync()引发InterruptedException{
返回threadpool.submit(新的可调用(){
@凌驾
公共整数调用()引发异常{
返回42;
}
});
}
public Future dostufflambdasync()引发InterruptedException{
返回线程池。提交(()->77);
}
公共静态void main(字符串[]args)引发异常{
应用程序app=新应用程序();
System.out.println(“Future value=“+app.doStuffAsync().get());
System.out.println(“未来值(lambda)=”+app.dostufflambdasync().get());
}
}
package de.scrum\u master.aspect;
导入java.util.concurrent.Callable;
导入org.aspectj.lang.ProceedingJoinPoint;
导入org.aspectj.lang.annotation.Around;
导入org.aspectj.lang.annotation.Aspect;
导入io.opentelemetry.context.context;
@面貌
公共类跟踪方面{
@大约(“调用(*java.util.concurrent.ExecutorService.submit(*)和&args(可调用)”)
公共对象wrapExecutor(ProceedingJoinPoint pjp,Callable Callable)抛出Throwable{
系统输出打印LN(pjp);
Context=Context.current();
返回pjp.procedure(新对象[]{context.wrap(可调用)});
}
}
调用(Future java.util.concurrent.ExecutorService.submit(可调用)) 未来价值=42 调用(Future java.util.concurrent.ExecutorService.submit(可调用)) 未来价值(λ)=77

当然,如果您能够准确地确定这些属性(例如,从哪些类中调用了哪些包等),那么您也可以过滤被拦截的调用以获得具有特定属性的可调用项。问题:(1)您使用的是Spring AOP(因为我看到了
@可配置的
注释)还是本机AspectJ?(2) 
上下文
类是什么样子的?没有它,我无法运行您的代码。我已经看到你做错了什么,但是为了详细说明并提出一个替代方案,我需要首先回答上面的问题。1)我使用的是原生AspectJ w/Loadtime编织2)上下文是OpenTelemetry跟踪库()的一部分。但是,上下文的细节是不相关的,因为上下文wrap(Callable Callable)返回一个可调用的对象。您只需通过just(Callable)pjp.procedure()返回构造的Callable,问题仍然是一样的。最好让人们帮助您决定什么是相关的,因为如果您知道得更好,您可能可以自己解决问题。答案总是提问或回答问题的最佳方式。例如,我下面的答案是MCVE。我只是使用编译时而不是加载时编织,以便不必共享aop.xml。我在我的示例中使用了OpenTelemetry,这使它比任何虚拟示例都更适合您,我想。这非常有效!谢谢