通过 上一篇博客 可以知道静态javaagent的使用必须在启动项目时加上javagent的相关启动参数,并且premain()方法也总是在main()方法之前执行,因此有一一定的局限性,在java6之后则做出了改变,有另一种方式可以在main方法启动之后执行,而且不需要添加jvm的启动参数
动态javaagent使用步骤
任意编写一个类,类中有如下两个方法之一即可(若两个都有,多参的优先级高)
12public static void agentmain(String arg, Instrumentation instrumentation){}public static void agentmain(String arg){}示例代码:
123public static void agentmain(String args, Instrumentation agentmain) {System.out.println(String.format("系统载入agentmain 参数%s 载入方法:premain", args));}动态的agent相对于静态agent需要添加两个依赖,构建打包时在pom文件中加入如下配置,让该类在打包成jar包时他的MANIFEST.MF文件中有代表该类为Agent类的信息
123456789101112131415161718192021222324252627282930313233343536373839<dependencies><!-- 使用atch时要引入--><dependency><groupId>com.tl.viptest</groupId><artifactId>jconsole</artifactId><version>1.8.0</version><scope>system</scope><systemPath>${java.home}/../lib/jconsole.jar</systemPath></dependency><dependency><groupId>com.tl.viptest</groupId><artifactId>tools</artifactId><version>1.8.0</version><scope>system</scope><systemPath>${java.home}/../lib/tools.jar</systemPath></dependency></dependencies><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-jar-plugin</artifactId><version>2.2</version><configuration><archive><manifestEntries><Project-name>${project.name}</Project-name><Project-version>${project.version}</Project-version><Boot-Class-Path>javassist-3.18.1-GA.jar</Boot-Class-Path><Agent-Class>上面编写的agent类全路径</Agent-Class><Can-Redefine-Classes>true</Can-Redefine-Classes></manifestEntries></archive><skip>true</skip></configuration></plugin></plugins></build>在
标签下加上Agent类的全路径,完成后将该类所在的项目打成jar包
参数含义:123456#动态agent 类Agent-Class: com.agenttest.DynamicAgent#agent 依懒包逗号分割Boot-Class-Path: javassist-3.18.1-GA.jar#是否允许重复装载Can-Redefine-Classes: true测试
执行main方法获取当前运行的jvm的进程id,之后睡眠当前线程,将获取到的进程id填入agentAttach()的局部变量targetPid,之后执行agentAttach()方法12345678910111213141516171819202122import com.sun.tools.attach.VirtualMachine;import org.junit.Ignore;import org.junit.Test;import java.lang.management.ManagementFactory;public class MyAgentTest {public static void main(String[] args) throws InterruptedException {System.out.println("输出进程ID:"+ ManagementFactory.getRuntimeMXBean().getName());while (true) {Thread.sleep(100);}}public void agentAttach() throws Exception {String targetPid = "16004";//这里输入main方法获取到的进程idVirtualMachine vm = VirtualMachine.attach(targetPid);vm.loadAgent("jar包所在磁盘路径", "传入的参数");}}输出结果如下:
结论: 动态agent可以在main方法启动后执行,并且启动的main方法不需要加任何的jvm启动参数
流程总结及与静态agent对比
1. 静态动态agent都是首先任意编写一个agent类,区别是静态agent类中必须要有premain()方法,动态agent类中必须要有agentmain()方法
2. 将该类打成jar包,静态agent的jar中MANIFEST.MF必须要有Premain-Class,对于动态agent的jar中MANIFEST.MF必须要有Agent-Class
3. 运行时静态agent的premain方法会在main方法之前执行,并且执行main方法是必须添加对应的jvm启动参数,动态agent的agentmain方法在main方法启动后才会执行,并且无需main方法所在类添加jvm启动参数