【问题标题】:Failing to change the argument value using bytebuddy无法使用 bytebuddy 更改参数值
【发布时间】:2020-10-08 07:42:14
【问题描述】:

我正在尝试使用 bytebuddy 添加查询参数以请求 url 这是我的代码:

new AgentBuilder.Default()
    .disableClassFormatChanges()
    .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
    .type(hasSuperType(named("org.springframework.web.client.RestTemplate")))
    .transform(new Transformer.ForAdvice().include(MyByteBuddy.class.getClassLoader())
    .advice(ElementMatchers.named("execute"), "agent.RestTemplateAdvice"))
    .installOn(instrumentation);

建议是

@Advice.OnMethodEnter
public static void before(@Advice.AllArguments Object[] args) {
    System.out.println("!!!!!!!!!!!");
    String data = args[0].toString();
    data = (data + "asdgb?param=myparam");
    System.out.println(data);
    args[0] = (Object)data;
    System.out.println(args[0]);
}

我得到的输出是

!!!!!!!!!!!
http://localhost:8086/movies/5678asdgb?param=myparam
http://localhost:8086/movies/5678

我也尝试过以下建议,但这个甚至没有捕获方法调用。

@Advice.OnMethodEnter
public static void before(@Advice.Argument(0) String argument) {
    System.out.println("!!!!!!!!!!!");
    argument = (argument + "asdgb?param=myparam");
    System.out.println(argument);
}

【问题讨论】:

  • 要不要同时显示你要改造的目标类+方法呢?查看RestTemplate javadoc,有三个execute 方法,但其中只有两个具有String 作为第一个参数,第三个具有URI。也许你称之为那个,这就是建议不匹配的原因。但我现在只是推测,因为我没有看到目标类以及它是如何被调用的。
  • 感谢@kriegaex。我能够解决这个问题。谷歌搜索后。正确的做法是 @Advice.Argument(value = 0, typing = Typing.DYNAMIC, readOnly = false) 字符串参数。设置后,我可以更改参数值。
  • 是的,也许可以,但您的代码仍然无法为 execute 方法运行,因为您将第一个参数更改为始终为 String。所以你的解决方案不稳定。要么限制匹配器中的方法签名,要么扩展您的建议以正确处理 URI 案例。这只是一个 hacky 解决方案。

标签: java byte-buddy javaagents


【解决方案1】:

就像你说的,为了改变你需要readOnly = false的参数。但就像我说的,您的建议并未涵盖所有三种execute() 方法。对于将URI 作为第一个参数的人,您将获得类转换异常。以下是解决方法:

帮助类使您的示例代码编译:

public class MyByteBuddy {}
import org.springframework.web.client.RestTemplate;

public class MyRestTemplate extends RestTemplate {}

ByteBuddy 建议:

import net.bytebuddy.asm.Advice;

import java.net.URI;
import java.net.URISyntaxException;

import static net.bytebuddy.implementation.bytecode.assign.Assigner.Typing.DYNAMIC;

public class RestTemplateAdvice {
  @Advice.OnMethodEnter()
  public static void before(
    @Advice.Argument(value = 0, typing = DYNAMIC, readOnly = false) Object url
  )
    throws URISyntaxException
  {
    String newURL = url.toString() + "search?q=scrum";
    url = url instanceof URI ? new URI(newURL) : newURL;
    System.out.println(url);
  }
}

驱动程序应用:

import net.bytebuddy.agent.ByteBuddyAgent;
import net.bytebuddy.agent.builder.AgentBuilder;
import org.springframework.web.client.HttpClientErrorException;

import java.lang.instrument.Instrumentation;
import java.net.URI;
import java.net.URISyntaxException;

import static net.bytebuddy.matcher.ElementMatchers.hasSuperType;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static org.springframework.http.HttpMethod.GET;

class BBChangeRestTemplateReturnValue_64257928 {
  public static void main(String[] args) throws URISyntaxException {
    applyAdvice();
    performSampleRequests();
  }

  private static void applyAdvice() {
    Instrumentation instrumentation = ByteBuddyAgent.install();
    new AgentBuilder.Default()
      .disableClassFormatChanges()
      .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
      .type(hasSuperType(named("org.springframework.web.client.RestTemplate")))
      .transform(
        new AgentBuilder.Transformer.ForAdvice()
          .include(MyByteBuddy.class.getClassLoader())
          .advice(named("execute"), "RestTemplateAdvice")
      )
      .installOn(instrumentation);
  }

  private static void performSampleRequests() throws URISyntaxException {
    try {
      new MyRestTemplate().execute("https://www.google.com/", GET, null, null);
    }
    catch (HttpClientErrorException ignored) {}
    try {
      new MyRestTemplate().execute(new URI("https://www.google.com/"), GET, null, null);
    }
    catch (HttpClientErrorException ignored) {}
  }
}

控制台日志:

https://www.google.com/search?q=scrum
https://www.google.com/search?q=scrum

【讨论】:

    【解决方案2】:

    使用@AllArguments 时的问题是您正在分配一个值

    args[0] = (Object) data;
    

    这对 Byte Buddy 的模板功能没有帮助。实际上,这意味着您正在将所有参数读入一个数组,将data 分配给该数组的第一个索引,然后不再使用它。相反,您需要:

    Object[] _args = args;
    _args[0] = (Object) data;
    args = _args;
    

    虽然这在 Java 代码中似乎没有意义,但它会转换为您想要的字节码,其中所有参数都被分配了所提供数组的值。然而,执行 kriegaex 建议使用索引基代理作为参数会更有效。

    【讨论】:

    • 好收获!我没有检查第一个代码 sn-p,专注于 @Argument(0) 变体。但我确实记得几个月前当我与@AllArguments 踏入同一个陷阱时你是如何帮助我的,不知道重新分配的违反直觉的必要性。我仍在 Sarek 的几个 BB 建议中使用它,例如。 here。我的源代码评论甚至链接回我们对此的讨论,所以我不会忘记它。 ?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多