Java 使用bytebuddy更改参数值失败

Java 使用bytebuddy更改参数值失败,java,byte-buddy,javaagents,Java,Byte Buddy,Javaagents,我正在尝试使用bytebuddy向请求url添加查询参数 这是我的密码: new AgentBuilder.Default() .disableClassFormatChanges() .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION) .type(hasSuperType(named("org.springframework.web.client.RestTemplate")))

我正在尝试使用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);
}

正如您所说,要更改参数,您需要
readOnly=false
。但正如我所说,您的建议并没有涵盖所有三种
execute()
方法。如果将
URI
作为第一个参数,则会得到类强制转换异常。以下是如何修复它:

使示例代码编译的助手类:

公共类MyByteBuddy{}
导入org.springframework.web.client.restemplate;
公共类MyRestTemplate扩展了RestTemplate{}
ByteBuddy建议:

import net.bytebuddy.asm.Advice;
导入java.net.URI;
导入java.net.URISyntaxException;
导入静态net.bytebuddy.implementation.bytecode.assign.Assigner.Typing.DYNAMIC;
公共类RestTemplateAdvice{
@建议.OnMethodEnter()
公共静态无效之前(
@参数(值=0,类型=DYNAMIC,只读=false)对象url
)
抛出URISyntaxException
{
字符串newURL=url.toString()+“搜索?q=scrum”;
url=url实例的URI?新URI(newURL):newURL;
System.out.println(url);
}
}
驱动程序应用程序:

导入net.bytebuddy.agent.ByteBuddyAgent;
导入net.bytebuddy.agent.builder.AgentBuilder;
导入org.springframework.web.client.HttpClientErrorException;
导入java.lang.instrument.Instrumentation;
导入java.net.URI;
导入java.net.URISyntaxException;
导入静态net.bytebuddy.matcher.ElementMatchers.hasSuperType;
导入静态net.bytebuddy.matcher.ElementMatchers.named;
导入静态org.springframework.http.HttpMethod.GET;
类BBChangeRestTemplateReturnValue_64257928{
公共静态void main(字符串[]args)抛出URISyntaxException{
applyAdvice();
performSampleRequests();
}
私有静态void applyAdvice(){
Instrumentation Instrumentation=ByteBuddyAgent.install();
新建AgentBuilder.Default()
.disableClassFormatChanges()
.与(AgentBuilder.重新定义策略.重新转换)
.type(hasSuperType(命名为(“org.springframework.web.client.restemplate”))
.变换(
新代理builder.Transformer.ForAdvice()
.include(MyByteBuddy.class.getClassLoader())
.advice(命名为(“执行”),“restemplateadvice”)
)
.installOn(仪器仪表);
}
私有静态void performSampleRequests()引发URISyntaxException{
试一试{
新建MyRestTemplate()。执行(“https://www.google.com/,GET,null,null);
}
catch(忽略HttpClientErrorException){}
试一试{
新建MyRestTemplate().execute(新URI(“https://www.google.com/)、GET、null、null);
}
catch(忽略HttpClientErrorException){}
}
}
控制台日志:

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

使用
@AllArguments
时的问题是您正在分配这样的值

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

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

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

还可以显示要转换的目标类+方法吗?查看,有三个
execute
方法,但其中只有两个具有
String
作为第一个参数,第三个具有
URI
。也许你称之为那个,这就是为什么建议不匹配的原因。但我现在只是在猜测,因为我看不到目标类及其名称。谢谢@kriegaex。我能解决这个问题。在谷歌搜索之后。正确的方法是@Advice.Argument(value=0,typing=typing.DYNAMIC,readOnly=false)字符串参数。设置此参数后,我可以更改参数值。是的,可能是这样,但您的代码仍然不会为
execute
方法运行
URI
作为第一个参数,因为您将第一个参数更改为始终为
字符串。所以你的解决方案是不稳定的。限制匹配器中的方法签名或扩展建议以正确处理
URI
案例。这只是一个简单的解决方案。很好!我没有检查第一个代码段,而是集中在
@参数(0)
变量上。但我确实记得几个月前,当我与
@AllArguments
陷入同样的陷阱时,你是如何帮助我的,当时我不知道重新分配工作的必要性是违反直觉的。我还在沙瑞克的几个BB建议中使用这个,例如。我的源代码评论甚至链接到我们关于这个的讨论,所以我不会忘记它。