我正在尝试使用字节伙伴来编写Java工具代理。我的目标是用我自己的代理调用替换java标准库方法调用。建议使用Byte Buddy的MemberSubstitution
实现此目的。我使用SO的this和this问题作为参考。
我正在使用Intellij IDEA进行编码。我的代理代码分为以下多个文件:
MyFirstAgent.java
public class MyFirstAgent {
public static void premain(String agentArgs, Instrumentation inst) {
new AgentBuilder.Default()
.type(ElementMatchers.any())
.transform(new ByteBuddyTransformer())
.with(AgentBuilder.Listener.StreamWriting.toSystemOut())
.with(AgentBuilder.TypeStrategy.Default.REDEFINE)
.installOn(inst);
}
ByteBuddyTransformer.java
public class ByteBuddyTransformer implements AgentBuilder.Transformer {
@Override
public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription,
ClassLoader classLoader, JavaModule javaModule) {
try {
return builder.visit(MemberSubstitution.relaxed()
.method(named("add"))
.replaceWith(MyClass.class.getMethod("printLine"))
.on(any()));
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
return builder;
}
}
MyClass.java
public class MyClass {
public boolean printLine(){
System.out.println("This is the proxy!");
return true;
}
}
并且我要检测的应用程序在另一个具有以下内容的Intellij IDEA项目中:
Main.java
public class Main {
public static void main(String[] args) {
ClassToMonitor classToMonitor = new ClassToMonitor();
classToMonitor.bar();
}
}
ClassToMonitor.java
package com.company;
import java.util.ArrayList;
import java.util.Arrays;
public class ClassToMonitor {
public void bar() {
// create an empty array list with an initial capacity
ArrayList<Integer> arrlist = new ArrayList<Integer>(5);
// use add() method to add elements in the list
arrlist.add(15);
// print all the elements available in list
for (Integer number : arrlist) {
System.out.println("Number = " + number);
}
}
}
当我构建代理的胖子并用我的应用程序运行它时,出现以下错误:
[Byte Buddy] ERROR com.company.ClassToMonitor [jdk.internal.loader.ClassLoaders$AppClassLoader@2626b418, unnamed module @385e9564, loaded=false]
java.lang.IllegalStateException: Cannot invoke public boolean com.company.MyClass.printLine() on [class java.util.ArrayList, E]
如果需要,我可以提供完整的错误消息。另外,我通常对Java和Instrumentation还是陌生的,因此在这里我可能会遗漏一些基本知识,请原谅并指出是这种情况。
为了使替换生效,目标方法需要接受与替换方法相同的参数,在您的情况下为int
。另外,由于您正在调用成员,因此该类的隐式第一个参数必须是接收器类型,即ArrayList
或任何超级类型,甚至是Object
。另外,您的替换方法必须是静态的:
public class MyClass {
public static boolean printLine(Object ignored, int ignored2){
System.out.println("This is the proxy!");
return true;
}
}
MemberSubstitution仍然不如预期的那样灵活。但是,如果需要的话,您可以使用chained
步骤注入自定义字节码。