系列文章目录
01 在方法体的开头或结尾插入代码
02 使用Javassist实现方法执行时间统计
前言
上一章我们介绍了使用Javassist实现一个简单的Hello World程序,学会了Javassist创建类和新增方法。本章主要介绍如何使用Javassist实现一个无侵入的方法执行时间统计
引入Javassist jar包
在上一篇文章已经创建了一个maven工程并引入了javassist的jar包,如果你是第一次观看本系列文章,也可以复制以下maven依赖将jar包导入工程。
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.28.0-GA</version>
</dependency>
使用Javassist实现方法执行时间统计
/**
* 【Javassist】快速入门系列02 使用Javassist实现方法执行时间统计
* 公众号&B站:精致的王同学
* @author 精致的王同学
* @date 2022/12/19 17:23
*/
public class Basic02ExecTime {
public static void main(String[] args) throws Exception{
// 获取javassist的类池
ClassPool pool = ClassPool.getDefault();
// 获取basic.StockService javassist的ctClass文件
CtClass ctClass = pool.get("basic.StockService");
// 获取声明的queryStockNum方法
CtMethod method = ctClass.getDeclaredMethod("queryStockNum");
// 为StockService类新增stime和etime字段
CtField stime = new CtField(CtClass.longType, "stime", ctClass);
CtField etime = new CtField(CtClass.longType, "etime", ctClass);
ctClass.addField(stime);
ctClass.addField(etime);
// 在方法体之前插入代码
method.insertBefore("stime = System.currentTimeMillis();");
// 在方法体之后插入代码
method.insertAfter("etime = System.currentTimeMillis();\n" +
"System.out.println(\"执行时长:\"+(etime - stime)+\"毫秒\");");
// 将类写成文件
ctClass.writeFile();
// 获取生成后的类文件
Class<?> clazz = ctClass.toClass();
// 创建新的实例
Object obj = clazz.newInstance();
// 获取生成后的queryStockNum方法
Method queryStockNum = clazz.getDeclaredMethod("queryStockNum", Long.class);
// 调用生成后的queryStockNum方法
queryStockNum.invoke(obj,(Object) 1l);
}
}
以上Basic02ExecTime类创建了一个main方法,该方法中首先获取javassist的类池pool,然后调用pool.get(“basic.StockService”)方法获取到basic包下的StockService类。StockService类源码如下:
/**
* 库存业务类
* 公众号&B站:精致的王同学
* @author 精致的王同学
* @date 2022/12/19 17:23
*/
public class StockService {
public Integer queryStockNum(Long skuId) throws Exception {
// 调用库存数量接口
Random random = new Random();
Long mills = Long.valueOf(random.nextInt(5)*1000);
// 模拟接口调用耗时
Thread.sleep(mills);
return 0;
}
}
该类主要模拟获取sku库存数量的逻辑。使用Thread.sleep(mills)模拟接口调用耗时。
回到BasicExecTime 的main方法中,在获取到basic.StockService的ctClass对象后,获得了其声明的queryStockNum方法的CtMethod对象。
然后通过调用new CtField(CtClass type, String name, CtClass declaring) 为StockService类声明了两个字段。其中CtField构造方法的第一个参数代表字段的类型,第二个参数代表字段名称,最后一个参数代表新增字段的CtClass对象。
声明这两个字段的原因是Javassist不允许在插入的代码块中方法方法的局部变量。
之后便是调用 method.insertBefore(“stime = System.currentTimeMillis();”); 方法在方法体前插入获取当前时间毫秒数的代码,并将其赋给变量stime 。insertBefore方法的参数为字符串类型的代码块。
最后调用 method.insertAfter(“etime = System.currentTimeMillis();\n” +
“System.out.println(“执行时长:”+(etime - stime)+“毫秒”);”);方法在方法体之后插入代码,将方法执行的时间打印在屏幕上。
剩下的代码为模拟调用javassist修改后的queryStockNum方法。输出结果为:
总结
本篇文章介绍了使用Javassist实现方法执行时间统计,同时学习了使用Javassist在方法体的开头/结尾插入代码和为类添加字段。Javassist可以在无侵入的前提下在方法体前后加入内容,非常的方便。