「字节码插桩」统计方法耗时(第二篇:崭露头角)- 第312篇

一、何为字节码插桩

我们知道JVM是不能直接执行.java 代码,也不能直接执行.class文件,它只能执行.class 文件中存储的指令码。这就是为什么class需要通过classLoader 装载以后才能运行。基于此机制可否在ClassLoader装载之前拦截修改class当中的内容(jvm 指令码)从而让程序中包含我们的埋点逻辑呢? 答案是肯定的,但需要用到两个技术 javaagentjavassist 。前者用于拦截ClassLoad装载,后者用于操作修改class文件。

 

二、javaagent

2.1 javaagent介绍

javaagent 是java1.5之后引入的特性,其主要作用是在class 被加载之前对其拦截,以插入我们的监听字节码

2.2 javaagent jar

javaagent 最后展现形式是一个Jar包,有以下特性:

1)必须 META-INF/MANIFEST.MF中指定Premain-Class 设定启agent启动类。

2)在启类需写明启动方法 public static void main(String arg,)

3)不可直接运行,只能通过 jvm 参数-javaagent:xxx.jar 附着于其它jvm 进程运行。

 

三、javaagent使用

3.1 编写agent方法

         新建一个项目,然后新建一个Agent类:

  1. package com.kfit;
  2. import java.lang.instrument.Instrumentation;
  3. public class MyAgent {
  4. /**
  5. * jvm 参数形式启动,运行此方法
  6. *
  7. * @param agentArgs
  8. * @param inst
  9. */
  10. public static void premain(String agentArgs, Instrumentation inst) {
  11. System.out.println("Hello javaagent permain:"+agentArgs);
  12. }
  13. /**
  14. * 动态 attach 方式启动,运行此方法
  15. *
  16. * @param agentArgs
  17. * @param inst
  18. */
  19. public static void agentmain(String agentArgs, Instrumentation inst) {
  20. System.out.println("Hello javaagent agentmain");
  21. }
  22. }

对于Agent有两种使用方式:

jvm 参数形式:调用 premain 方法

attach 方式:调用 agentmain 方法

其中 jvm 方式,也就是说要使用这个 agent 的目标应用,在启动的时候,需要指定 jvm 参数 -javaagent:xxx.jar,当我们提供的 agent 属于基础必备服务时,可以用这种方式

 

当目标应用程序启动之后,并没有添加-javaagent加载我们的 agent,依然希望目标程序使用我们的 agent,这时候就可以使用 attach 方式来使用。

         在接下来我们讲解下jvm参数的配置方式。

 

3.2 添加premain-class参数

         在pom.xml文件添加如下配置:

  1. <plugin>
  2. <groupId>org.apache.maven.plugins</groupId>
  3. <artifactId>maven-jar-plugin</artifactId>
  4. <version>2.2</version>
  5. <configuration>
  6. <archive>
  7. <manifestEntries>
  8. <Project-name>${project.name}</Project-name>
  9. <Project-version>${project.version}</Project-version>
  10. <Premain-Class>com.kfit.MyAgent1</Premain-Class>
  11. <Can-Redefine-Classes>true</Can-Redefine-Classes>
  12. <Can-Retransform-Classes>true</Can-Retransform-Classes>
  13. </manifestEntries>
  14. </archive>
  15. <skip>true</skip>
  16. </configuration>
  17. </plugin>

参数说明:

Premain-Class:必填,agent启动

classCan-Redefine-Classes:默认为false ,是否允许重新定义

classCan-Retransform-Classes:默认为false,是否允许重置Class,重置后相当于class 从classLoade中清除,下次有需要的时候会重新装载,也会重新走Transformer 流程。

Boot-Class-Path:agent 所依赖的jar 路径,多个用空格分割(这个配置我们之后使用到)

 

 

3.3 构建打包

         使用maven的clean package打包出来一个jar文件:

agentdemo-0.0.1-SNAPSHOT.jar

 

3.4 使用agent

在任一JAVA应用中 添加jvm 参数并启动:

-javaagent:/data/tmp/agentdemo-0.0.1-SNAPSHOT.jar=angel

         对于我们之前的那个MeiMei类,不需要修改任何代码,配置vm options就可以启动看下效果:

Hello javaagent permain:angel

shopping:出发去和美眉一起逛街购物!

shopping:和美眉一起回家!

花了多少钱:5000.0

         看到打印结果没有,确实我们的这个main方法在执行之前打印出来了我们的那个代码。那么怎么使用javaagent编写一个可以统计耗时的呐,我们下节揭晓。

购买完整视频,请前往:http://www.mark-to-win.com/TeacherV2.html?id=287