Test before = new Test();
before.doNothing();
Profiler profiler = Profiler.getInstance();
profiler.inject();
Test after = new Test();
after.doNothing();
这是测试代码。 Test 类有一个函数,实际上什么也不做,字节码只是一个返回。探查器注入打印 20 的打印语句的字节码。但它不会受到任何更改的影响。我尝试使用自定义类加载器加载它,并且我能够创建该自定义加载类的新实例并打印 20,但这并没有覆盖实际的 Test 类,而是创建了一个额外的类,并且该类的 doNothing实际的测试类仍然相同,什么也不做。因此,我知道修改本身是有效的,只是没有正确应用。我需要一种方法以某种方式使用字节数组中的新类(如果有帮助的话也可以将其写入临时文件中)以某种方式全局更新类,并且它应该在运行时发生,没有实际的覆盖或编译时的东西.
instrumentation 来完成。这是一个例子:
Greeter#greet()
方法转换为打印
"Goodbye, World!"
而不是
"Hello, World!"
。
Launcher-Agent-Class
注册代理,这是特定于可执行 JAR 的。请参阅之前链接的文档以获取替代方案。
源代码
Greeter.java
package com.example;
public class Greeter {
public void greet() {
System.out.println("Hello, World!");
}
}
Main.java
package com.example;
public final class Main {
public static void main(String[] args) {
var greeter = new Greeter();
greeter.greet();
ExampleAgent.retransformGreeterClass();
greeter.greet();
}
}
示例Agent.java
package com.example;
import static java.lang.classfile.ClassTransform.transformingMethodBodies;
import java.lang.classfile.ClassFile;
import java.lang.classfile.CodeBuilder;
import java.lang.classfile.CodeElement;
import java.lang.classfile.Instruction;
import java.lang.classfile.MethodModel;
import java.lang.classfile.Opcode;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
import java.lang.instrument.UnmodifiableClassException;
import java.security.ProtectionDomain;
public class ExampleAgent {
private static Instrumentation inst;
public static void agentmain(String agentArgs, Instrumentation inst) {
ExampleAgent.inst = inst;
}
public static void retransformGreeterClass() {
inst.addTransformer(new GreeterClassFileTransformer(), true);
try {
inst.retransformClasses(Greeter.class);
} catch (UnmodifiableClassException ex) {
throw new RuntimeException(ex);
}
}
private static class GreeterClassFileTransformer implements ClassFileTransformer {
@Override
public byte[] transform(
ClassLoader loader,
String className,
Class<?> classBeingRedefined,
ProtectionDomain protectionDomain,
byte[] classfileBuffer) {
if ("com/example/Greeter".equals(className)) {
var cf = ClassFile.of();
return cf.transform(
cf.parse(classfileBuffer),
transformingMethodBodies(this::isGreetMethod, this::acceptGreetMethodElement));
}
return null;
}
private boolean isGreetMethod(MethodModel model) {
return model.methodName().equalsString("greet");
}
private void acceptGreetMethodElement(CodeBuilder builder, CodeElement element) {
if (element instanceof Instruction i && i.opcode() == Opcode.LDC) {
builder.ldc("Goodbye, World!");
} else {
builder.with(element);
}
}
}
}
清单清单.MF
Main-Class: com.example.Main
Launcher-Agent-Class: com.example.ExampleAgent
Can-Retransform-Classes: true
项目目录<PROJECT-DIR>
|
\---src
+---com
| \---example
| ExampleAgent.java
| Greeter.java
| Main.java
|
\---META-INF
MANIFEST.MF
构建与执行<PROJECT-DIR>
。编译:
javac --enable-preview --release 22 --source-path src -d out/classes src/com/example/*.java
包装:
jar cfm out/example.jar src/META-INF/MANIFEST.MF -C out/classes .
执行:
java --enable-preview -jar out/example.jar
输出Hello, World!
Goodbye, World