如何解决当对构造函数使用@Around 建议时,AspectJ weaver 给了我“不兼容的返回类型应用于构造函数执行”
我正在尝试使用@Around 建议将用于跟踪的上下文注入到新创建的 Callable 对象中:
@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());
}
}
当编织者遇到一个预期的连接点时,例如下面例子中的匿名 Callable 实现:
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;
}
});
}
}
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))
我在这里做错了什么?
解决方法
首先,你不能在构造函数 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
当然,如果您能够准确地确定这些调用,您也可以过滤具有某些属性的可调用调用的拦截调用,例如从哪些类在哪些包中进行调用等。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。