在没有 JVM Arg 的测试中加载 Java 代理

Load Java Agent In Tests Without JVM Arg

每次我 运行 我的 IDE 测试我都会得到错误

java.lang.IllegalStateException: Missing the '-javaagent' JVM argument. 

如果我创建一个 运行 配置并添加 jvm arg,一切都很好。但是,下次我 运行 一个不是预先配置的 运行 配置的动态测试时,我再次收到错误。

我想也许我可以使用 @BeforeClass 来动态加载 java 代理。例如

@BeforeClass
public void loadAgent() {
    String nameOfRunningVM = ManagementFactory.getRuntimeMXBean().getName();
    String pid = nameOfRunningVM.substring(0, nameOfRunningVM.indexOf('@'));
    VirtualMachine vm = VirtualMachine.attach(pid);

    Class klass = JavaAgent.class;
    CodeSource codeSource = klass.getProtectionDomain().getCodeSource();
    String agentJar = codeSource.getLocation().getPath();

    vm.loadAgent(agentJar, "");
    vm.detach();
}

(也许使用测试监听器可能意味着我不会将代码复制并粘贴到每个测试中 class)

这行得通,但似乎不太正确。

有没有不需要 pid 的方法来做到这一点?

或者有更好的替代方法吗?

此代码不可移植,但对于测试用例,这可能是可以接受的。没有其他方法可以将 Java 代理加载到已经 运行 HotSpot/OpenJDK 的环境中。

This answer 显示了通过 PID 附加不起作用的情况的回退(因为 RuntimeMXBean 的实际名称模式未指定)。它归结为设置一个特殊的“魔法”系统 属性 并遍历所有本地 JVM,查询它们的系统属性以找到您刚刚定义的系统:

String magic=UUID.randomUUID().toString()+'/'+System.nanoTime();
System.setProperty("magic", magic);
VirtualMachine vm;
for(VirtualMachineDescriptor vd:VirtualMachine.list()) try {
    vm=VirtualMachine.attach(vd);
    if(magic.equals(vm.getSystemProperties().getProperty("magic"))) break;
    vm.detach();
}

但显然,直接附加到所需的进程效率更高。

从 Java9 开始,您不再需要依赖 RuntimeMXBean 的未指定名称模式:

VirtualMachine vm = VirtualMachine.attach(Long.toString(ProcessHandle.current().pid()));

另一方面,self-attach 现在通常需要您在命令行上设置系统 属性 jdk.attach.allowAttachSelf 或使用更深入的技巧,如 [=16= 中讨论的那样].

对于 JDK8,EA Agent Loader 是更好的选择并且不需要 pid。 但是,它已停止用于 JDK9。

Kotlin 示例,其中我的代理恰好称为 JavaAgent!

AgentLoader.loadAgentClass(JavaAgent::class.java.name, "")