问题描述
我有一个简单的应用程序,它在循环中创建和使用 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();
}
}
}
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
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
控制台中看到了代理生成的 println
,MyApplication
控制台和代理控制台中都没有错误。
但是,在代理完成后,我仍然在 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 (将#修改为@)