无法使用Byte Buddy更改参数值。

huangapple go评论79阅读模式
英文:

Failing to change the argument value using bytebuddy

问题

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]);
}

Output received:

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

Alternative approach attempted:

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

I am trying to add query params to request url using bytebuddy
here is my code:

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);

and advice is

@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]);
}

output I am getting is

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

I have tried below advice too but this one is not even capturing the method call.

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

答案1

得分: 1

就像你说的那样为了改变参数你需要使用 `readOnly = false`。但是就像我所说的你的建议并没有涵盖所有三个 `execute()` 方法对于那个以 `URI` 作为第一个参数的方法你会得到类转换异常以下是修复方法

**辅助类以使你的示例代码能够编译通过**

```java
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

<details>
<summary>英文:</summary>
Like you said, in order to change the argument you need `readOnly = false`. But like I said, your advice does not cover all three `execute()` methods. You would get class cast exceptions for the one taking an `URI` as a first parameter. Here is how to fix it:
**Helper classes to make your sample code compile:**
```java
public class MyByteBuddy {}
import org.springframework.web.client.RestTemplate;

public class MyRestTemplate extends RestTemplate {}

ByteBuddy advice:

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() + &quot;search?q=scrum&quot;;
    url = url instanceof URI ? new URI(newURL) : newURL;
    System.out.println(url);
  }
}

Driver application:

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(&quot;org.springframework.web.client.RestTemplate&quot;)))
      .transform(
        new AgentBuilder.Transformer.ForAdvice()
          .include(MyByteBuddy.class.getClassLoader())
          .advice(named(&quot;execute&quot;), &quot;RestTemplateAdvice&quot;)
      )
      .installOn(instrumentation);
  }

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

Console log:

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

答案2

得分: 1

使用@AllArguments的问题在于你是这样赋值的

args[0] = (Object) data;

从字节码模板的角度来看,这并没有帮助。实际上,这意味着你将所有的参数读入一个数组中,将data赋值给该数组的第一个索引,然后再也没有使用它。相反,你需要这样做:

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

虽然在Java代码中这似乎没有意义,但它会转换成你想要的字节码,其中所有参数都被赋予了所提供数组的值。然而,按照kriegaex的建议,为参数使用基于索引的代理可能更加高效。

英文:

The problem when using @AllArguments is that you are assigning a value as such

args[0] = (Object) data;

This does not help in terms of Byte Buddy's templating capabilities. In effect, this means that you are reading all arguments into an array, assigning data to the first index of that array and then never use it again. Instead, you would need to:

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

While this does not seem to make sense in Java code, it translates into the byte code you want where all arguments are assigned the values of the supplied array. It would however be much more efficient to do what kriegaex suggests to use index base proxies for arguments.

huangapple
  • 本文由 发表于 2020年10月8日 15:42:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/64257928.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定