Instrumentation简介
Java探针技术,通过Instrumentation
,开发者可以构建一个独立于应用程序的代理程序(Agent),用来监测和协助运行在JVM上的程序,甚至能够替换和修改某些类的定义而对业务代码没有侵入
成熟应用
java领域的APM(Application Performance Management应用性能管理)工具,几乎都是基于Instrumentation实现的。
- zipkin:Twitter公司开源的一个分布式追踪工具,被Spring Cloud Sleuth集成,使用广泛而稳定
- skywalking:中国人吴晟(华为)开源的一款分布式追踪,分析,告警的工具,现在是Apache旗下开源项目
- cat:大众点评开源的一款分布式链路追踪工具。
Arthas
是Alibaba开源的Java诊断工具也是基于此。
这么多优秀的产品都在使用java探针Instrumentation
,是不是有兴趣深入了解一下呢。
使用流程
入口代理类
在main方法前执行
public class MyAgent {
static Instrumentation instrumentation;
public static void premain(String agentArgs, Instrumentation inst)
throws Exception {
try {
instrumentation = inst;
inst.addTransformer(new MyClassFileTransformer());
} catch (Exception e) {
}
}
}
需要提供一下两个方法之一,如果两个的话,前一个优先级较高
public static void premain(String agentOps, Instrumentation instrumentation);
public static void premain(String agentOps);
自定义ClassFileTransformer
package top.soft1010.java.myagent;
import javassist.*;
import javassist.expr.ExprEditor;
import javassist.expr.MethodCall;
import javassist.scopedpool.ScopedClassPool;
import javassist.scopedpool.ScopedClassPoolRepositoryImpl;
import java.io.IOException;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
/**
* Created by bjzhangjifu on 2021/9/27.
*/
public class MyClassFileTransformer implements ClassFileTransformer {
@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
System.out.println("Transforming " + className);
//非自有类, 直接返回
if (!className.startsWith("top/soft1010")) {
return classfileBuffer;
}
//为null,表示由bootstrapLoader加载的,不能重写
if (loader == null) {
return classfileBuffer;
}
try {
// if (className != null && className.indexOf("/") != -1) {
// className = className.replaceAll("/", ".");
// }
CtClass ctClass = ClassPool.getDefault().makeClass(new java.io.ByteArrayInputStream(classfileBuffer));
CtMethod[] methods = ctClass.getDeclaredMethods();
for (CtMethod method : methods) {
try {
method.insertAfter("System.out.println("--after--");");
method.insertBefore("System.out.println("--before--");");
} catch (CannotCompileException e) {
e.printStackTrace();
}
}
return ctClass.toBytecode();
} catch (Exception e) {
e.printStackTrace();
}
return classfileBuffer;
}
}
ClassFileTransformer用于修改class文件,该操作发生在 JVM 加载 class 之前。它只有一个transform
方法,实现该方法可以修改 class字节码(这里可以使用javassist,asm等),并返回修改后的 class字节码,有两点要注意:
- 如果此方法返回null, 表示我们不对类进行处理直接返回。否则,会用我们返回的byte[]来代替原来的类
- ClassFileTransformer必须添加进Instrumentation才能生效 Instrumentation#addTransformer(ClassFileTransformer)
- 当存在多个Transformer时,一个Transformer调用返回的byte数组将成为下一个Transformer调用的输入
运行
这里我们直接写一个main方法类,当然也可以是jar
public class MyTest {
public static void main(String[] args) {
new MyTest().test();
}
private void test() {
System.out.println(" ----- test ----- ");
}
}
执行命令
java -javaagent:myagent.jar top.soft1010.java.myagent.MyTest
面试
1、加载类的时候,对字节码进行修改?
能,使用java探针,javaagent就可以。
JavaAgent 是JDK 1.5 以后引入的,也可以叫做Java代理。
JavaAgent 是运行在 main方法之前的拦截器,它内定的方法名叫 premain ,也就是说先执行 premain 方法然后再执行 main 方法。
premain方法有个Instrumentation 参数,通过它我们可以添加自定义一个FileTransformer,这个接口只有一个方法transform 通过这个方法我们能获取加载类的字节码,当然也可以修改编辑字节码,比如在指定类的方法前后添加监控信息等。
2、说说java探针技术javaagent的应用
skywalking:中国人吴晟(华为)开源的一款分布式追踪,分析,告警的工具,现在是Apache旗下开源项目
cat:大众点评开源的一款分布式链路追踪工具。
arthas: 阿里开源的java诊断工具
当然好多大公司内部也有好多自己定制的apm工具也是基于此
源码
文中涉及到的代码 源码