Java:在附加检测期间无法重新定义类

问题描述

我有一个简单的应用程序,它在循环中创建和使用 tochange 类的实例:

public class tochange {
  private int i;
  public tochange(int i) {
    this.i = i;
  }

  public void print() {
    System.out.println(i);
  }
}
public class MyApplication {
  public static void main(String[] args) {
    System.out.println("pid = " + ProcessHandle.current().pid());
    int i = 0;
    // This is an instance for the whole application lifecycle
    var wholeAppInstance = new tochange(0);

    while (true) {
      // Use the wholeAppInstance first
      wholeAppInstance.print();

      // Create and use a new instance in each loop
      new tochange(++i).print();

      System.out.println("------------");
      try {
        Thread.sleep(3000);
      } catch (InterruptedException e) {
        e.printstacktrace();
      }
    }
  }
}

应用程序运行时,控制台中有这样几行:

0
1
------------
0
2
------------
0
3
------------

我还有一个 attach 应用程序,用于加载代理并将其附加到正在运行的 JVM。 它需要两个参数,第一个是正在运行的 JVM 进程的 pid,第二个是将附加的代理的路径:

// The main class of the attach application
public class Main {
  public static void main(String[] args) throws IOException,AttachNotSupportedException,AgentLoadException,AgentinitializationException {
    String processId = args[0];
    String agentJar = args[1];
    VirtualMachine virtualMachine = VirtualMachine.attach(processId);
    try {
      virtualMachine.loadAgent(agentJar);
    } catch (Exception error) {
      error.printstacktrace();
    } finally {
      virtualMachine.detach();
    }
  }
}

最后,我有了代理,它重新定义了 tochange 类:

public class MyAgentMain {
  public static void agentmain(String arg,Instrumentation instrumentation) throws Exception {
    System.out.println("Agent mark 1");
    Class<?> tochange = Class.forName("org.abc.security.attach.application.tochange");

    System.out.println("Agent mark 2");
    ByteArrayOutputStream output = new ByteArrayOutputStream();

    try (InputStream input = MyAgentMain
            .class
            .getResourceAsstream("/org/abc/security/attach/application/tochange.class")) {
      System.out.println("input reference: " + input);
      byte[] buffer = new byte[1024];
      int length;
      while ((length = input.read(buffer)) != -1) {
        output.write(buffer,length);
      }
      System.out.println("Agent mark 3");
    } catch (Exception error) {
      error.printstacktrace();
    }

    instrumentation.redefineClasses(new ClassDeFinition(tochange,output.toByteArray()));
    System.out.println("Agent mark 4");
  }
}

代理的 MANIFEST.MF 包含以下内容

Manifest-Version: 1.0
Created-By: Maven Jar Plugin 3.2.0
Build-Jdk-Spec: 11
Agent-Class: org.abc.security.attach.agent.MyAgentMain
Can-redefine-Classes: true

并将 tochange 类更改为以下内容

public class tochange {
  private int i;

  public tochange(int i) {
    this.i = i;
  }

  public void print() {
    System.out.println("Changed:" + this.i);
  }
}

我执行附加应用程序,传递正在运行的 MyApplication 的 pid 和代理位置:

java --add-modules jdk.attach -jar attach-0.1.0-SNAPSHOT.jar $PID agent-0.1.0-SNAPSHOT.jar

我在 MyApplication 控制台中看到了代理生成printlnMyApplication 控制台和代理控制台中都没有错误

但是,在代理完成后,我仍然在 MyApplication 中看到相同的输出

------------
0
7
------------
Agent mark 1
Agent mark 2
input reference: java.io.BufferedInputStream@540e6237
Agent mark 3
Agent mark 4
0
8
------------
0
9
------------

我希望由于没有错误,检测成功,Changeto 类被重新定义并且 println 不同。 然而,事实并非如此......

我在做什么(或期望)错了?


编辑来回答我的问题

@johannes-kuhn 的回复给了我尝试更改代理中重新定义的类位置的想法,以免与原始路径在同一路径下。

似乎最终的检测 MyApplication 的“资源”保持不变,因为重新定义的类与旧的类在资源中具有相同的路径。更改代理资源中 tochange 类的位置,成功了!

为了清楚起见,这里是有效的 MyAgentMain

public class MyAgentMain {
  public static void agentmain(String arg,Instrumentation instrumentation) throws Exception {
    System.out.println("Agent mark 1");
    Class<?> license = Class.forName("org.abc.security.attach.application.tochange");

    System.out.println("Agent mark 2");
    ByteArrayOutputStream output = new ByteArrayOutputStream();

    try (InputStream input = MyAgentMain
            .class
            .getResourceAsstream("/tochange.class")) { // THE NON-WORKING VERSION HAD /org/abc/security/attach/application/tochange.class
      System.out.println("input reference: " + input);
      byte[] buffer = new byte[1024];
      int length;
      while ((length = input.read(buffer)) != -1) {
        output.write(buffer,length);
      }
      System.out.println("Agent mark 3");
    } catch (Exception error) {
      error.printstacktrace();
    }

    instrumentation.redefineClasses(new ClassDeFinition(license,output.toByteArray()));
    System.out.println("Agent mark 4");
  }
}

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)