没有合适的资源?快使用搜索试试~ 我知道了~
手把手教你实现一个Java Agent.pdf
需积分: 5 0 下载量 131 浏览量
2023-11-20
11:16:32
上传
评论
收藏 4.44MB PDF 举报
温馨提示
试读
27页
手把手教你实现一个Java Agent
资源推荐
资源详情
资源评论
手把手教你实现一个Java Agent
故事的小黄花
团队中有同事在做性能优化相关的工作,因为公司基础设施不足,同事在代码中写了大量的代码
统计某个方法的耗时,大概的代码形式就是
@Override
public void method(Req req) {
StopWatch stopWatch = new StopWatch();
stopWatch.start("某某方法-耗时统计");
method()
stopWatch.stop();
log.info("查询耗时分布:{}", stopWatch.prettyPrint());
}
这样的代码非常多,侵入性很大,联想到之前学习的Java Agent技术,可以无侵入式地解决这类
问题,所以做了一个很小很小的demo
Instrumentation
在 了 解 A g e n t 之 前 需 要 先 看 看 I n s t r u m e n t a t i o n
J D K 从 1 . 5 版 本 开 始 引 入 了 j a va . l a n g . i n s t r u m e n t 包 , 该 包 提 供 了 一 些 工 具 帮 助 开 发 人 员 实 现 字
节 码 增 强 , I n s t r u m e n t a t i o n 接 口 的 常 用 方 法 如 下
public interface Instrumentation {
/**
* 注册Class文件转换器,转换器用于改变Class文件二进制流的数据
*
* @param transformer 注册的转换器
* @param canRetransform 设置是否允许重新转换
*/
void addTransformer(ClassFileTransformer transformer, boolean canRetransform);
/**
* 移除一个转换器
*
* @param transformer 需要移除的转换器
*/
boolean removeTransformer(ClassFileTransformer transformer);
/**
* 在类加载之后,重新转换类,如果重新转换的方法有活跃的栈帧,那些活跃的栈帧继续运行未转换前的方法
*
* @param 重新转换的类数组
*/
void retransformClasses(Class<?>... classes) throws UnmodifiableClassException;
/**
* 当前JVM配置是否支持重新转换
*/
boolean isRetransformClassesSupported();
/**
* 获取所有已加载的类
*/
@SuppressWarnings("rawtypes")
Class[] getAllLoadedClasses();
}
public interface ClassFileTransformer {
// className参数表示当前加载类的类名,classfileBuffer参数是待加载类文件的字节数组
// 调用addTransformer注册ClassFileTransformer以后,后续所有JVM加载类都会被它的transform方法拦截
// 这个方法接收原类文件的字节数组,在这个方法中做类文件改写,最后返回转换过的字节数组,由JVM加载这个修改过
// 如果transform方法返回null,表示不对此类做处理,如果返回值不为null,JVM会用返回的字节数组替换原来类的字
byte[] transform( ClassLoader loader,
String className,
Class<?> classBeingRedefined,
ProtectionDomain protectionDomain,
byte[] classfileBuffer)
throws IllegalClassFormatException;
}
I n st r u me nt a t io n有 两 种 使 用 方 式
0. 在 J VM启 动 的 时 候 添 加 一 个 A gent j a r 包
1. J VM运 行 以 后 在 任 意 时 刻 通 过 At t a c h A P I 远 程 加 载 Ag ent的 j a r 包
Agent
使 用 J av a Ag en t 需 要 借 助 一 个 方 法 , 该 方 法 的 方 法 签 名 如 下
public static void premain (String agentArgs, Instrumentation instrumentation) {
}
从字面上理解,就是运行在main()函数之前的类。在Java虚拟机启动时,在执行main()函数之
前,会先运行指定类的premain()方法,在premain()方法中对class文件进行修改,它有两个入参
0. agentArgs:启动参数,在JVM启动时指定
1. instrumentation:上文所将的Instrumentation的实例,我们可以在方法中调用上文所讲的方
法,注册对应的Class转换器,对Class文件进行修改
如下图,借助Instrumentation,JVM启动时的处理流程是这样的:J VM会执行指定类的
premain()方法,在premain()中可以调用Instrumentation对象的addTransformer方法注册
ClassFileTransformer。当JVM加载类时会将类文件的字节数组传递给ClassFileTransformer的
transform方法,在transform方法中对Class文件进行解析和修改,之后JVM就会加载转换后的
Class文件
J V M 启 动 时 的 处 理 流 程
那我们需要做的就是写一个转换Class文件的ClassFileTransformer,下面用一个计算函数耗时的
小例子看看Java Agent是怎么使用的
public class MyClassFileTransformer implements ClassFileTransformer {
@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, Prote
if ("com/example/aop/agent/MyTest".equals(className)) {
// 使用ASM框架进行字节码转换
ClassReader cr = new ClassReader(classfileBuffer);
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_FRAMES);
ClassVisitor cv = new TimeStatisticsVisitor(Opcodes.ASM7, cw);
cr.accept(cv, ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG);
return cw.toByteArray();
}
return classfileBuffer;
}
}
public class TimeStatisticsVisitor extends ClassVisitor {
public TimeStatisticsVisitor(int api, ClassVisitor classVisitor) {
super(Opcodes.ASM7, classVisitor);
}
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, St
MethodVisitor mv = cv.visitMethod(access, name, descriptor, signature, exceptions);
if (name.equals("<init>")) {
return mv;
}
return new TimeStatisticsAdapter(api, mv, access, name, descriptor);
}
}
剩余26页未读,继续阅读
资源评论
阿拉伯梳子
- 粉丝: 1165
- 资源: 5391
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功