java - 是否可以使用 ByteBuddy Advice 机制将本机方法封装在线程中?
问题描述
我试图在单独的线程中延迟写入套接字,以使用 javaagent 模拟网络堆栈中的延迟。使用 ByteBuddy,我设法装饰了原生java.net.SocketOutputStream::writeSocket0
. 但是,这样做,我暂停了当前线程,这不是我想要实现的。为了避免这个问题,我想创建一个可以调用本机方法的线程。不幸的是,我对 ByteBuddy 的了解仍然相当有限,我没有设法找到实现这一目标的方法。我的最佳尝试如下所示:
//premain function
public static void premain(final String agentArgs, final Instrumentation instrumentation) {
try{
BootLoaderInjector.inject(DelayedExecutor.class);
long duration = 100;
new AgentBuilder.Default()
.with(new ByteBuddy().with(Implementation.Context.Disabled.Factory.INSTANCE))
.with(AgentBuilder.InitializationStrategy.NoOp.INSTANCE)
.with(AgentBuilder.TypeStrategy.Default.REBASE)
.enableNativeMethodPrefix("somePrefix")
.with(AgentBuilder.Listener.StreamWriting.toSystemError().withErrorsOnly())
.ignore(nameStartsWith("net.bytebuddy."))
.type(named("java.net.SocketOutputStream"))
.transform((transformer, typeDescription, classLoader, javaModule) ->
transformer.method(named("socketWrite0"))
.intercept(
Advice.withCustomMapping().bind(Duration.class, duration)
.to(DelayInterceptor.class)
)
)
.installOn(instrumentation);
}
catch (Throwable t){
System.err.printf(t.getMessage());
}
}
// interceptor class
public class DelayInterceptor {
@Advice.OnMethodEnter
public static void intercept(@SuperCall Callable<?> caller, @Duration long duration) {
DelayedExecutor.execute(callable, duration);
}
}
// execute the intercepted method in a new thread with a delay
public class DelayedExecutor implements Runnable {
private final Callable<?> callable;
private final long delay;
private DelayedExecutor(Callable<?> callable, long delay) {
this.callable = callable;
this.delay = delay;
}
public static void execute(Callable<?> callable, long delay){
new Thread(new DelayedExecutor(callable, delay)).start();
}
@Override
public void run() {
try {
Thread.sleep(this.delay);
this.callable.call();
} catch (Exception ignore) {}
}
}
但是,这会失败,因为@Callable
它不是Advice
API 的一部分。
不使用建议似乎会造成严重的限制。实际上使用 MethodDelegation 会引发以下错误:
Registration of auxiliary types was disabled: net.bytebuddy.implementation.auxiliary.MethodCallProxy@3206bfce
有没有办法使用Advice
ByteBuddy 的 API 将函数调用包装在块内?
编辑
为了增加清晰度并解决答案建议,这里是它的实现BootLoaderInjector.inject
,显示了如何将类型添加到依赖于引导加载程序的类加载器中ClassInjector.UsingUnsafe.ofBootLoader()
:
public class BootLoaderInjector {
public static void inject(Class<?> targetClass) {
try {
ClassInjector.UsingUnsafe.ofBootLoader().inject(singletonMap(
new TypeDescription.ForLoadedType(targetClass),
ClassFileLocator.ForClassLoader.read(targetClass)
));
} catch (Throwable e){
System.out.println("Something went terribly wrong: " + e.getMessage());
e.printStackTrace();
}
}
}
解决方案
您需要注册一个InjectionStrategy
以将这些所需的类型注入目标类加载器。例如,对于引导加载程序,这可能是一个UsingInstrumentation
. 更高效,但依赖 JVM 内部 API,您还可以使用UsingUnsafe
.
推荐阅读
- java - 仅具有队列通信和性能测试的春季应用程序
- oracle - PL/SQL: For 循环选择列表
- visual-studio - Visual Studio 无法识别项目中的命名空间
- azure - 使用 strictfilter 调用 QnA api 时出现 Http 500 错误
- plsql - 如何将变量从存储过程传递到另一个存储过程
- c# - 有时渲染,有时不渲染的图像
- javascript - clearvalidators 不能在 Angular 6 表单控件中工作
- python - 将数据集传递给 keras 时如何在数据集上初始化迭代器
- recursion - 在 C# 中创建递归方法
- mysql - GROUP_CONCAT 和连接,如何也包括不匹配的行