Java 一次执行多次运行的字节伙伴转换

Java 一次执行多次运行的字节伙伴转换,java,instrumentation,byte-buddy,javaagents,Java,Instrumentation,Byte Buddy,Javaagents,我编写了一个javaagent,如下所示,以捕获apacheorg.apache.http.client.HttpClient的execute方法的执行时间。它正在捕获时间,但它运行了三次 import java.lang.instrument.Instrumentation; import net.bytebuddy.agent.builder.AgentBuilder; import net.bytebuddy.implementation.MethodDelegation; impor

我编写了一个
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;
    }
}

控制台输出:

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>
我不确定是什么原因导致它打印了3次

更新:
感谢@kriegaex的帮助,您可以在中找到它。

首先,您从哪里获得
实现接口
元素匹配器?它不是ByteBuddy的一部分,至少在当前版本中不是。我将其替换为
isSubTypeOf
,以使代码能够编译。无论如何,如果你像这样激活日志记录

newagentbuilder.Default()
.with(AgentBuilder.Listener.StreamWriting.toSystemError().with TransformationsOnly())
.with(AgentBuilder.InstallationListener.StreamWriting.toSystemError())
.type(isSubTypeOf(HttpClient.class))
// ...
您将在控制台上看到如下内容:

[Byte Buddy]安装net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer$bytebuddy之前$ModuleSupport@1eb5174b在太阳仪上。InstrumentationImpl@67080771
[Byte Buddy]安装net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer$bytebuddy$ModuleSupport@1eb5174b在太阳仪上。InstrumentationImpl@67080771
[Byte Buddy]TRANSFORM org.apache.http.impl.client.DefaultHttpClient[jdk.internal.loader.ClassLoaders$AppClassLoader@2626b418,未命名模块@4d518b32,已加载=false]
[Byte Buddy]TRANSFORM org.apache.http.impl.client.AbstractHttpClient[jdk.internal.loader.ClassLoaders$AppClassLoader@2626b418,未命名模块@4d518b32,已加载=false]
[Byte Buddy]TRANSFORM org.apache.http.impl.client.CloseableHttpClient[jdk.internal.loader.ClassLoaders$AppClassLoader@2626b418,未命名模块@4d518b32,已加载=false]
处理响应:HTTP/1.1 200 OK[日期:Mon,2020年10月26日04:46:18 GMT,Expires:-1,缓存控制:private,max age=0,内容类型:text/html;charset=ISO-8859-1,P3P:CP=“这不是P3P策略!有关更多信息,请参阅g.co/p3phelp。”,服务器:gws,X-XSS-Protection:0,X-Frame-Options:SAMEORIGIN,Set Cookie:1P_JAR=2020-10-26-04;expires=Wed,25-Nov-2020 04:46:18 GMT;path=/;domain=.google.com;Secure,Set Cookie:NID=204=qecfpmmsawxytgneon07twiw4eypiarm67ok5nhyuowaq38qjz7gw9k8nz5zuguhoyadck-nsakz8tgad5mdplaxevenwwwwwqrDiecj-nafga4rhenz7w7w7wfR0pc4lvlAUx3ssJk;expires=Tue,2021年4月27日04:46:18 GMT;path=/;domain=.google.com;HttpOnly,接受范围:无,变化:接受编码,传输编码:分块]org.apache.http.conn。BasicManagedEntity@6928f576
拍摄于1870年
拍摄于1871年
拍摄于1871年
看到了吗?您已经在整个类层次结构中安装了拦截器,共有三个类:

因此,您要么需要限制元素匹配器,要么必须使用多个日志行

免责声明:我不是ByteBuddy专家,也许Rafael Winterhalter稍后会写出更好的答案,也许我的解决方案不规范

截取器似乎匹配所有类,即使从技术上讲,该方法仅在
CloseableHttpClient
中定义,而未在
AbstractHttpClient
DefaultHttpClient
中重写。限制类型匹配的一种方法是检查目标类是否实际包含要插入的方法:

ElementMatcher.Junction executeMethodDescription=isMethod()
.和(命名为(“执行”))
.和(不是(isAbstract()))
.及(详情(三))
和(takesArgument(0,命名为(“org.apache.http.client.methods.HttpUriRequest”))
和(takesArgument(1,命名为(“org.apache.http.client.ResponseHandler”))
和(takesArgument(2个,命名为(“org.apache.http.protocol.HttpContext”));
新建AgentBuilder.Default()
.with(AgentBuilder.Listener.StreamWriting.toSystemError().with TransformationsOnly())
.with(AgentBuilder.InstallationListener.StreamWriting.toSystemError())
.类型(
isSubTypeOf(HttpClient.class)
.和(声明方法(执行方法描述))
)
.transform((生成器、类型、类加载器、模块)->builder
.方法(ExecuteMethodDescription)
.intercept(MethodDelegation.to(TimingInterceptor.class))
)
.installOn(仪器仪表);
现在,控制台日志应该变成:

[Byte Buddy]安装net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer$bytebuddy之前$ModuleSupport@67080771在太阳仪上。InstrumentationImpl@72cde7cc
[Byte Buddy]安装net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer$bytebuddy$ModuleSupport@67080771在太阳仪上。InstrumentationImpl@72cde7cc
[Byte Buddy]TRANSFORM org.apache.http.impl.client.CloseableHttpClient[jdk.internal.loader.ClassLoaders$AppClassLoader@2626b418,未命名模块@6ea2bc93,已加载=false]
处理响应:HTTP/1.1 200 OK[日期:周一,2020年10月26日05:21:25 GMT,过期时间:-1,缓存控制:私有,最大年龄=0,内容类型:text/html;charset=ISO-8859-1,P3P:CP=“这不是P3P策略!有关更多信息,请参阅g.co/p3phelp。”,服务器:gws,X-XSS-Protection:0,X-Frame-Options:SAMEORIGIN,Set Cookie:1P_JAR=2020-10-26-05;expires=Wed,2020-11-25 05:21:25 GMT;path=/;domain=.google.com;Secure,Set Cookie:NID=204=n7u6sbbwww5jjm1hynbowchqtymsbilhwhiokyuspvhzjjjkirasvpmeilhipo34baq5qqljnd7gdd7gdd1ivd16ghizlee7k3mcloxny9wgn9c6elhijjjjj6mpujjjjjjjjjjjjjjjjjjjjjjjjjjjjv9w9w9vjjjjjj3JIXu-AV84WKDxdc;expires=Tue,2021年4月27日05:21:25 GMT;path=/;domain=.google.com;HttpOnly,接受范围:无,变化:接受编码,传输编码:分块]org.apache.http.conn。BasicManagedEntity@5471388b
拿了1274

更新:实际上,
intercept()
的行为描述如下:

通过提供的实现实现先前定义或匹配的方法。方法截取通常在以下方法之一中实现
<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>
java -javaagent:"javaagent.jar" -jar application.jar