Java Agent(二)Attach机制及运行时加载agent
引入
从JDK6以来,我们可以通过agentmain方法进行对JVM中运行的程序进行监控。然鹅不像premain由JVM启动时被调用,这个agentmain方法调用的时机是什么?我们又如何发送一个信号给目标虚拟机?
简介
agentmain方法调用的时机,自然是我们在一个进程中所触发的,它通过发送信号告诉另外一个进程,也就是目标JVM进程,此时要加载我们指定的agent。这个过程中,涉及进程间通信,在进程间传递命令与参数。 JDK提供了一种方式去实现这种通信:Attach API。它的核心类为VirtualMachine,其中的attach方法利用了UDS(Unix Domain socket)原理实现了Linux中进程间的通信。
Demo
书接上文
- Agent类新增agentmain方法
public class Agent {
public static void premain(String args, Instrumentation ins) {
System.out.printf("Pre-Main called , args : " + args);
ins.addTransformer(new Transformer());
}
public static void agentmain(String args, Instrumentation ins) {
System.out.println("Agent-Main called , args : " + args);
Class<?>[] cls = ins.getAllLoadedClasses();
for (Class<?> cl : cls) {
if (cl.getName().equals("com.example.javaagent.demo.controller.Demo")) {
System.out.println("Find Demo.Class ! ");
String fileName = "... .../Demo.class";
byte[] bytes = getBytesFromFile(fileName);
ClassDefinition classDefinition = new ClassDefinition(cl, bytes);
try {
ins.redefineClasses(classDefinition);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (UnmodifiableClassException e) {
e.printStackTrace();
}
}
}
}
public static byte[] getBytesFromFile(String fileName) {
File file = new File(fileName);
Long filelength = file.length();
byte[] filecontent = new byte[filelength.intValue()];
try {
FileInputStream in = new FileInputStream(file);
in.read(filecontent);
in.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return filecontent;
}
}
- 将Demo工程的JVM参数-javaagent去掉,正常启动java进程 3.写一个attach方法并运行,其中的pid为Demo工程的java进程号
public static void main(String[] args) {
try {
VirtualMachine virtualMachine = VirtualMachine.attach(String.valueOf(pid));
virtualMachine.loadAgent("... .../agent.jar=testArg");
} catch (Exception e) {
e.printStackTrace();
}
}
运行后Demo工程将输出两行日志:
Agent-Main called , args : testArg Find Demo.Class !
此时再请求接口,类字节码已被转换:
