APM(2)动态javaagent使用

通过 上一篇博客 可以知道静态javaagent的使用必须在启动项目时加上javagent的相关启动参数,并且premain()方法也总是在main()方法之前执行,因此有一一定的局限性,在java6之后则做出了改变,有另一种方式可以在main方法启动之后执行,而且不需要添加jvm的启动参数

动态javaagent使用步骤

  1. 任意编写一个类,类中有如下两个方法之一即可(若两个都有,多参的优先级高)

    1
    2
    public static void agentmain(String arg, Instrumentation instrumentation){}
    public static void agentmain(String arg){}

    示例代码:

    1
    2
    3
    public static void agentmain(String args, Instrumentation agentmain) {
    System.out.println(String.format("系统载入agentmain 参数%s 载入方法:premain", args));
    }
  2. 动态的agent相对于静态agent需要添加两个依赖,构建打包时在pom文件中加入如下配置,让该类在打包成jar包时他的MANIFEST.MF文件中有代表该类为Agent类的信息

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    <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包
    参数含义:

    1
    2
    3
    4
    5
    6
    #动态agent 类
    Agent-Class: com.agenttest.DynamicAgent
    #agent 依懒包逗号分割
    Boot-Class-Path: javassist-3.18.1-GA.jar
    #是否允许重复装载
    Can-Redefine-Classes: true
  3. 测试
    执行main方法获取当前运行的jvm的进程id,之后睡眠当前线程,将获取到的进程id填入agentAttach()的局部变量targetPid,之后执行agentAttach()方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    import 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);
    }
    }
    @Ignore
    @Test
    public void agentAttach() throws Exception {
    String targetPid = "16004";//这里输入main方法获取到的进程id
    VirtualMachine vm = VirtualMachine.attach(targetPid);
    vm.loadAgent("jar包所在磁盘路径", "传入的参数");
    }
    }

    输出结果如下:

    mark

    结论: 动态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启动参数

-------------本文结束感谢您的阅读-------------