首先,我们需要注意:并不是所有的虚拟机,都支持从 command line 启动 Java Agent。
An implementation is not required to provide a way to start agents from the command-line interface. Link
问题:当前的 JVM implementation 是否支持从 command line 启动 Java Agent? 回答:一般都支持
其次,如何在 command-line 为 Agent Jar 添加参数信息呢?
命令行启动
Command-Line
从命令行启动 Java Agent 需要使用 -javagent
选项:
-javaagent:jarpath[=options]
jarpath
is the path to the agent JAR file.options
is the agent options.
┌─── -javaagent:jarpath
┌─── Command-Line ───┤
│ └─── -javaagent:jarpath=options
Load-Time Instrumentation ───┤
│ ┌─── MANIFEST.MF - Premain-Class: lsieun.agent.LoadTimeAgent
└─── Agent Jar ──────┤
└─── Agent Class - premain(String agentArgs, Instrumentation inst)
示例:
java -cp ./target/classes/ -javaagent:./target/TheAgent.jar=this-is-a-long-message sample.Program
Agent Jar
在 TheAgent.jar
中,依据 META-INF/MANIFEST.MF
里定义 Premain-Class
属性找到 Agent Class:
Premain-Class: lsieun.agent.LoadTimeAgent
The agent is passed its agent options
via the agentArgs
parameter.
public class LoadTimeAgent {
public static void premain(String agentArgs, Instrumentation inst) {
// ...
}
}
The agent options
are passed as a single string, any additional parsing should be performed by the agent itself.
示例一:读取 agentArgs
LoadTimeAgent.java
package lsieun.agent;
import java.lang.instrument.Instrumentation;
public class LoadTimeAgent {
public static void premain(String agentArgs, Instrumentation inst) {
System.out.println("Premain-Class: " + LoadTimeAgent.class.getName());
System.out.println("agentArgs: " + agentArgs);
System.out.println("Instrumentation Class: " + inst.getClass().getName());
}
}
运行
每次修改代码之后,都需要重新生成 .jar
文件:
mvn clean package
获取示例命令:
$ cd learn-java-agent
$ java -jar ./target/TheAgent.jar
Load-Time Usage:
java -javaagent:/path/to/TheAgent.jar sample.Program
Example:
java -cp ./target/classes/ sample.Program
java -cp ./target/classes/ -javaagent:./target/TheAgent.jar sample.Program
第一次运行,在使用 -javagent
选项时不添加 options
信息:
$ java -cp ./target/classes/ -javaagent:./target/TheAgent.jar sample.Program
Premain-Class: lsieun.agent.LoadTimeAgent
agentArgs: null
Instrumentation Class: sun.instrument.InstrumentationImpl
从上面的输出结果中,我们可以看到:
- 第一点,
agentArgs
的值为null
。 - 第二点,
Instrumentation
是一个接口,它的具体实现是sun.instrument.InstrumentationImpl
类。
第二次运行,在使用 -javagent
选项时添加 options
信息:
$ java -cp ./target/classes/ -javaagent:./target/TheAgent.jar=this-is-a-long-message sample.Program
Premain-Class: lsieun.agent.LoadTimeAgent
agentArgs: this-is-a-long-message
Instrumentation Class: sun.instrument.InstrumentationImpl
示例二:解析 agentArgs
我们传入的信息,一般情况下是 key-value
的形式,有人喜欢用 :
分隔,有人喜欢用 =
分隔:
username:tomcat,password:123456
username=tomcat,password=123456
LoadTimeAgent.java
package lsieun.agent;
import java.lang.instrument.Instrumentation;
public class LoadTimeAgent {
public static void premain(String agentArgs, Instrumentation inst) {
System.out.println("Premain-Class: " + LoadTimeAgent.class.getName());
System.out.println("agentArgs: " + agentArgs);
System.out.println("Instrumentation Class: " + inst.getClass().getName());
if (agentArgs != null) {
String[] array = agentArgs.split(",");
int length = array.length;
for (int i = 0; i < length; i++) {
String item = array[i];
String[] key_value_pair = getKeyValuePair(item);
String key = key_value_pair[0];
String value = key_value_pair[1];
String line = String.format("|%03d| %s: %s", i, key, value);
System.out.println(line);
}
}
}
private static String[] getKeyValuePair(String str) {
{
int index = str.indexOf("=");
if (index != -1) {
return str.split("=", 2);
}
}
{
int index = str.indexOf(":");
if (index != -1) {
return str.split(":", 2);
}
}
return new String[]{str, ""};
}
}
运行
第一次运行,使用 :
分隔:
$ java -cp ./target/classes/ -javaagent:./target/TheAgent.jar=username:tomcat,password:123456 sample.Program
Premain-Class: lsieun.agent.LoadTimeAgent
agentArgs: username:tomcat,password:123456
Instrumentation Class: sun.instrument.InstrumentationImpl
|000| username: tomcat
|001| password: 123456
第二次运行,使用 =
分隔:
$ java -cp ./target/classes/ -javaagent:./target/TheAgent.jar=username=jerry,password=12345 sample.Program
Premain-Class: lsieun.agent.LoadTimeAgent
agentArgs: username=jerry,password=12345
Instrumentation Class: sun.instrument.InstrumentationImpl
|000| username: jerry
|001| password: 12345
总结
本文内容总结如下:
- 第一点,在命令行启动 Java Agent,需要使用
-javaagent:jarpath[=options]
选项,其中的options
信息会转换成为premain
方法的agentArgs
参数。 - 第二点,对于
agentArgs
参数的进一步解析,需要由我们自己来完成。