【Agent】Javassist 增加方法执行耗时
1 前言
上节我们已经知道通过配置-javaagent:文件.jar后,在java程序启动时候会执行premain方法。接下来我们使用javassist字节码增强的方式,来监控方法程序的执行耗时。
2 Java agent
2.1 Javassist 是什么
Javassist是一个开源的分析、编辑和创建Java字节码的类库。是由东京工业大学的数学和计算机科学系的 Shigeru Chiba (千叶 滋)所创建的。它已加入了开放源代码JBoss应用服务器项目,通过使用Javassist对字节码操作为JBoss实现动态"AOP"框架。
另外关于java字节码的处理,目前有很多工具,如bcel,asm。不过这些都需要直接跟虚拟机指令打交道。如果你不想了解虚拟机指令,可以采用javassist。javassist是jboss的一个子项目,其主要的优点,在于简单,而且快速。直接使用java编码的形式,而不需要了解虚拟机指令,就能动态改变类的结构,或者动态生成类。
2.2 尝试
那我们开始,首先引入依赖,并打包到 jar 中:
pom.xml
<dependency> <groupId>javassist</groupId> <artifactId>javassist</artifactId> <version>3.12.1.GA</version> <type>jar</type> </dependency> <!-- 将javassist包打包到Agent中 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> </execution> </executions> <configuration> <artifactSet> <includes> <include>javassist:javassist:jar:</include> </includes> </artifactSet> </configuration> </plugin>
MyAgent.java
public class MyAgent { //JVM 首先尝试在代理类上调用以下方法 public static void premain(String agentArgs, Instrumentation inst) { System.out.println("this is my agent:" + agentArgs); MyMonitorTransformer monitor = new MyMonitorTransformer(); inst.addTransformer(monitor); } //如果代理类没有实现上面的方法,那么 JVM 将尝试调用该方法 public static void premain(String agentArgs) { } }
MyMonitorTransformer.java
/** * @author kuku */ public class MyMonitorTransformer implements ClassFileTransformer { @Override public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { try { String currentClassName = className.replaceAll("/", "."); // 根据包的前缀 拦截自己项目的前缀类 if (!currentClassName.startsWith("com.virtuous")) { // 提升classNameSet中含有的类 return null; } System.out.println("transform: [" + currentClassName + "]"); CtClass ctClass = ClassPool.getDefault().get(currentClassName); CtBehavior[] methods = ctClass.getDeclaredBehaviors(); for (CtBehavior method : methods) { enhanceMethod(method); } return ctClass.toBytecode(); } catch (Exception e) { e.printStackTrace(); } return null; } private void enhanceMethod(CtBehavior method) throws Exception { if (method.isEmpty()) { return; } String methodName = method.getName(); if ("main".equalsIgnoreCase(methodName)) { return; } final StringBuilder source = new StringBuilder(); // 前置增强: 打入时间戳 // 保留原有的代码处理逻辑 source.append("{") .append("long start = System.nanoTime();\n") //前置增强: 打入时间戳 .append("$_ = $proceed($$);\n") //调用原有代码,类似于method();($$)表示所有的参数 .append("System.out.print(\"method:[") .append(methodName).append("]\");").append("\n") .append("System.out.println(\" cost:[\" +(System.nanoTime() - start)+ \"ns]\");") // 后置增强,计算输出方法执行耗时 .append("}"); ExprEditor editor = new ExprEditor() { @Override public void edit(MethodCall methodCall) throws CannotCompileException { methodCall.replace(source.toString()); } }; method.instrument(editor); } }
测试执行效果:
3 小结
好啦,本节就到这里了,感觉还挺有意思,有理解不对的地方欢迎指正哈。
分类:
源码 / Agent
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· Obsidian + DeepSeek:免费 AI 助力你的知识管理,让你的笔记飞起来!
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
2023-06-06 【Redis】【持久化】RDB 快照是怎么实现的?
2023-06-06 【Redis】【持久化】AOF 持久化是怎么实现的?
2023-06-06 【Redis】Redis 数据类型详解
2023-06-06 【网络基础】Linux 系统是如何收发网络包的?
2023-06-06 【网络基础】键入网址到网页显示,期间发生了什么?
2023-06-06 【网络基础】TCP/IP 网络模型有哪几层?