一次执行多次运行的java Bytebuddy转换
我编写了一个javaagent
,如下所示,以捕获apache org.apache.http.client.HttpClient
的execute
方法的执行时间。它正在捕获时间,但它运行了三次
import java.lang.instrument.Instrumentation;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.implementation.MethodDelegation;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.isAbstract;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.not;
public class TimerAgent {
public static void premain(
String arguments,
Instrumentation instrumentation
) {
new AgentBuilder.Default()
.type(
implementsInterface(named("org.apache.http.client.HttpClient"))
)
.transform((builder, type, classLoader, module) ->
builder.method(isMethod()
.and(named("execute"))
.and(not(isAbstract()))
.and(takesArguments(3))
.and(takesArgument(0, named("org.apache.http.client.methods.HttpUriRequest")))
.and(takesArgument(1, named("org.apache.http.client.ResponseHandler")))
.and(takesArgument(2, named("org.apache.http.protocol.HttpContext"))))
.intercept(MethodDelegation
.to(TimingInterceptor.class))
).installOn(instrumentation);
}
}
import java.lang.reflect.Method;
import java.util.concurrent.Callable;
import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.implementation.bind.annotation.SuperCall;
public class TimingInterceptor {
@RuntimeType()
public static Object intercept(
@Origin Method method,
@SuperCall Callable<?> callable
) {
long start = System.currentTimeMillis();
try {
try {
return callable.call();
} catch (Exception e) {
e.printStackTrace();
}
} finally {
System.out.println(
"Took " + (System.currentTimeMillis() - start));
}
return 0;
}
}
我正在使用DefaultHttpClient
发出HTTP请求。
客户端代码:
import java.io.IOException;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.DefaultHttpClient;
public class Main {
public static void main(String[] args) throws IOException {
HttpClient client = new DefaultHttpClient();
HttpUriRequest httpUriRequest = new HttpGet("http://www.google.com");
HttpResponse response = client
.execute(httpUriRequest, new ResponseHandler<HttpResponse>() {
public HttpResponse handleResponse(final HttpResponse response)
throws ClientProtocolException, IOException {
return response;
}
});
}
}
控制台输出:
Took 512
Took 512
Took 512
Maven依赖项:
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>1.10.10</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpasyncclient</artifactId>
<version>4.1.4</version>
<scope>provided</scope>
</dependency>
This是implementsInterface
的实现
以下是我执行应用程序的方式:
java -javaagent:"javaagent.jar" -jar application.jar
我不确定是什么原因导致它打印了3次
更新: 感谢@kriegaex对MCVE的支持,可以在this GitHub repository中找到它
# 1 楼答案
首先,从哪里获得
implementsInterface
元素匹配器?它不是ByteBuddy的一部分,至少在当前版本中不是。为了使代码能够编译,我用isSubTypeOf
替换了它。不管怎样,如果你像这样激活日志记录您将在控制台上看到类似的内容:
看到了吗?您已经在整个类层次结构中安装了拦截器,共有三个类:
因此,您要么需要限制元素匹配器,要么必须使用多个日志行
免责声明:我不是ByteBuddy专家,也许Rafael Winterhalter稍后会写一个更好的答案,也许我的解决方案不规范
它看起来好像拦截器匹配所有类,尽管从技术上讲,该方法只在
CloseableHttpClient
中定义,而没有在AbstractHttpClient
或DefaultHttpClient
中重写。限制类型匹配的一种方法是检查目标类是否确实包含要插入的方法:现在控制台日志应该变成:
更新:实际上
intercept()
的行为在其javadoc中描述:更新2:我的完整MCVE可以在this GitHub repository为大家提供方便