文章目录
- 基本使用
- premain
- 使用场景
- agentmain
- 关于tools.jar
https://docs.oracle.com/en/java/javase/20/docs/specs/jvmti.html
com.sun的API,如果使用其他厂商的JVM,可能没有这个API了,比如Eclipse的J9
https://www.ibm.com/docs/en/sdk-java-technology/8?topic=documentation-java-attach-api
基本使用
Java Agent提供了一种在加载字节码时对字节码进行修改的能力,有两种实现方式:
- 在应用启动之时,通过
premain()
方法来实现在应用启动时侵入 - 针对运行中的JVM,通过Attach API和
agentmain()
方法来实现侵入
premain
通过JVM参数-javaagent:*.jar
启动应用。应用在启动时,会优先加载Java Agent,并执行premain()方法,这时部分的类都还没有被加载。此时,可以实现对新加载的类进行字节码修改,但如果premain()方法执行失败或者抛出异常,则JVM会被终止。
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
</manifest>
<manifestEntries>
<!--指定包含premain方法的类,需要配置为类的全路径名,必须配置-->
<Premain-Class>org.example.PreMainAgent</Premain-Class>
<!--是否可以重新定义class,默认为false,可选配置-->
<Can-Redefine-Classes>true</Can-Redefine-Classes>
<!--是否可以重新转换class,实现字节码替换,默认为false,可选配置-->
<Can-Retransform-Classes>true</Can-Retransform-Classes>
<!--是否可以设置Native方法的前缀,默认为false,可选配置-->
<Can-Set-Native-Method-Prefix>true</Can-Set-Native-Method-Prefix>
</manifestEntries>
</archive>
</configuration>
</plugin>
</plugins>
</build>
写一个入口类,注意方法名必须是premain
public class PreMainAgent {
public static void premain(String agentArgs, Instrumentation inst) {
System.out.println(agentArgs);
}
}
当两种方式都存在时,带有Instrumentation参数的方法的优先级更高,会被JVM优先加载
public static void premain(String agentArgs)
public static void premain(String agentArgs, Instrumentation inst)
主程序可以指定的agent的数量是没有任何限制的,但是会根据指定的先后顺序依次执行各个agent的逻辑。
使用场景
使用Idea启动项目时,可以看到启动命令加了-javaagent加载了idea_rt.jar这个agent
agentmain
attach模式不能通过添加启动参数的方式来连接agent和主程序,低版本的JDK需要单独引入tools.jar包,借助com.sun.tools.attach包下的VirtualMachine工具类。如果是高版本的JDK不需要引入单独引入tools.jar包。
attachmain方法只允许以下两种定义方式,当两种方式都存在时,带有Instrumentation参数的方法的优先级更高
public static void agentmain(String agentArgs)
public static void agentmain(String agentArgs, Instrumentation inst)
和premain不同的在于,需要在manifest签名文件中指定Agent-Class这个属性,可以在maven-jar-plugin像下面这样配置
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
</manifest>
<manifestEntries>
<!--指定包含agentmain方法的类,需要配置为类的全路径名,必须配置-->
<Agent-Class>org.example.AgentMain</Agent-Class>
<!--是否可以重新定义class,默认为false,可选配置-->
<Can-Redefine-Classes>true</Can-Redefine-Classes>
<!--是否可以重新转换class,实现字节码替换,默认为false,可选配置-->
<Can-Retransform-Classes>true</Can-Retransform-Classes>
<!--是否可以设置Native方法的前缀,默认为false,可选配置-->
<Can-Set-Native-Method-Prefix>true</Can-Set-Native-Method-Prefix>
</manifestEntries>
</archive>
</configuration>
</plugin>
</plugins>
</build>
获取到VirtualMachine实例后,可以通过loadAgent方法可以实现注入agent代理类的操作,该方法的第一个参数是agent的jar路径,第二个参数是传给agnet的参数。
关于tools.jar
java8及之前tools.jar随jdk一同安装
java8之后,tools.jar被移除,参考JEP 220