英文:
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() + "search?q=scrum";
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("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) {}
}
}
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论