java Agent
1. 介绍
Java Agent 是从 JDK1.5 开始引入的,算是一个比较老的技术了。Java Agent 其实就是 java 命令的一个参数(即 -javaagent)
-javaagent 参数之后需要指定一个 jar 包,这个 jar 包需要同时满足下面两个条件:
在 META-INF 目录下的 MANIFEST.MF 文件中必须指定 premain-class 配置项。
premain-class 配置项指定的类必须提供了 premain() 方法。
public static void premain(String agentArgs, Instrumentation inst){
...
}
在 Java 虚拟机启动时,执行 main() 函数之前,虚拟机会先找到 -javaagent 命令指定 jar 包,然后执行 premain-class 中的 premain() 方法。其实就是 main() 函数之前的一个拦截器。
2. 使用
创建java Agent 大致步骤
-
定义一个 MANIFEST.MF 文件,在其中添加 premain-class 配置项。
-
创建 premain-class 配置项指定的类,并在其中实现 premain() 方法,方法签名如下:
-
将 MANIFEST.MF 文件和 premain-class 指定的类一起打包成一个 jar 包。
-
使用 -javaagent 指定该 jar 包的路径即可执行其中的 premain() 方法。
eg:
premain() 方法有两个重载,如下所示,如果两个重载同时存在,一个参数的将会被忽略,两个参数的会被执行:
public class TestAgent {
public static void premain(String agentArgs,
Instrumentation inst) {
System.out.println("this is a java agent with two args");
System.out.println("参数:" + agentArgs + "\n");
}
public static void premain(String agentArgs) {
System.out.println("this is a java agent only one args");
System.out.println("参数:" + agentArgs + "\n");
}
}
源码如下:
# sun.instrument.InstrumentationImpl#loadClassAndStartAgent
private void loadClassAndStartAgent(String var1, String var2, String var3) throws Throwable {
ClassLoader var4 = ClassLoader.getSystemClassLoader();
Class var5 = var4.loadClass(var1);
Method var6 = null;
NoSuchMethodException var7 = null;
boolean var8 = false;
try {
var6 = var5.getDeclaredMethod(var2, String.class, Instrumentation.class);
var8 = true;
} catch (NoSuchMethodException var13) {
var7 = var13;
}
if (var6 == null) {
try {
var6 = var5.getDeclaredMethod(var2, String.class);
} catch (NoSuchMethodException var12) {
}
}
if (var6 == null) {
try {
var6 = var5.getMethod(var2, String.class, Instrumentation.class);
var8 = true;
} catch (NoSuchMethodException var11) {
}
}
if (var6 == null) {
try {
var6 = var5.getMethod(var2, String.class);
} catch (NoSuchMethodException var10) {
throw var7;
}
}
setAccessible(var6, true);
if (var8) {
var6.invoke((Object)null, var3, this);
} else {
var6.invoke((Object)null, var3);
}
setAccessible(var6, false);
}
创建 MANIFEST.MF 文件并打包,可以直接使用 maven-assembly-plugin 打包插件来完成这两项功能。在 pom.xml 中引入 maven-assembly-plugin 插件并添加相应的配置,如下所示:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.4</version>
<configuration>
<appendAssemblyId>false</appendAssemblyId>
<!-- 将TestAgent的所有依赖包都打到jar包中-->
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<!-- 添加MANIFEST.MF中的各项配置-->
<manifest>
<!-- 添加 mplementation-*和Specification-*配置项-->
<addDefaultImplementationEntries>true
</addDefaultImplementationEntries>
<addDefaultSpecificationEntries>true
</addDefaultSpecificationEntries>
</manifest>
<!-- 将 premain-class 配置项设置为com.xxx.TestAgent-->
<manifestEntries>
<Premain-Class>com.xxx.TestAgent</Premain-Class>
</manifestEntries>
</archive>
</configuration>
<executions>
<execution>
<!-- 绑定到package生命周期阶段上 -->
<phase>package</phase>
<goals>
<!-- 绑定到package生命周期阶段上 -->
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
打包
mvn package -Dcheckstyle.skip -DskipTests
在启动 TestMain 项目之前,需要在 VM options 中使用 -javaagent 命令指定前面创建的 test-agent.jar
- 参数:option1=value2,option2=value2