Javassist进行方法插桩
javassist官网 http://jboss-javassist.github.io/javassist/
javassist API网 http://jboss-javassist.github.io/javassist/html/index.html
javassist参考博客 https://www.ibm.com/developerworks/cn/java/j-dyn0916/
Ⅰ插桩
自动用例生成(使用Randoop)
评价(对用例筛选冗余)>功能覆盖、语句覆盖(一般用后者)
>插桩 (插入语句)
用Javassist实现自动插入语句,即插桩
方法层次的插桩:
在方法前插入进入方法的语句,方法后插入退出方法的语句
Ⅱ用Javassist对方法插桩
1‘ 首先在官网下Javassist的jar包,放置在web项目的lib下,在eclipse里右击项目选择build path的最后一个,导入jar包。
2’ 新建一个相对要插桩类a.java的被插桩程序aJssistTiming.java
弄明白这个对方法的插桩,还是得看程序,我对测试第二个博客里的Triangle.java代码进行方法插桩,在方法前后都插入一个输出语句,TriangleJssistTiming.java代码示例如下:
1 import java.io.IOException; 2 import javassist.*; 3 4 public class TriangleJssistTiming { 5 public static void main(String argv[]){ 6 try { 7 String classname="Triangle"; 8 //获取class文件 9 CtClass clas =ClassPool.getDefault().get(classname); 10 11 if(clas==null){ 12 System.out.println("classname"+clas+" not found."); 13 }else{ 14 //调用方法 15 addTiming(clas,"isTriangle"); 16 addTiming(clas,"getType"); 17 addTiming(clas,"diffOfBorders"); 18 addTiming(clas,"getBorders"); 19 clas.writeFile(); 20 } 21 } catch (CannotCompileException ex) { 22 ex.printStackTrace(); 23 } catch (NotFoundException ex) { 24 ex.printStackTrace(); 25 } catch (IOException ex) { 26 ex.printStackTrace(); 27 } 28 29 30 } 31 32 private static void addTiming(CtClass cct,String method) throws NotFoundException, CannotCompileException{ 33 //获取方法信息,如果方法不存在,则抛出异常 34 CtMethod ctMethod = cct.getDeclaredMethod(method); 35 //将旧的方法名称进行重新命名 36 String nname = method + "$impl"; 37 ctMethod.setName(nname); 38 //方法的副本,采用过滤器的方式 39 CtMethod newCtMethod = CtNewMethod.copy(ctMethod, method, cct, null); 40 41 //为该方法添加时间过滤器来计算时间,并判断获取时间的方法是否有返回值 42 String type = ctMethod.getReturnType().getName(); 43 StringBuffer body = new StringBuffer(); 44 body.append("{\n long start = System.currentTimeMillis();\n"); 45 46 body.append("System.out.println(\" "+ nname +"_in_11\");"); 47 //返回值类型不同 48 if(!"void".equals(type)) { 49 body.append(type + " result = "); 50 } 51 //可以通过$$将传递给拦截器的参数,传递给原来的方法 52 body.append(nname + "($$);\n"); 53 //输出方法运行时间差 54 // body.append("System.out.println(\"Call to method " + nname + " took \" + \n (System.currentTimeMillis()-start) + " + "\" ms.\");\n"); 55 body.append("System.out.println(\" "+ nname +"_out_11\");"); 56 if(!"void".equals(type)) { 57 body.append("return result;\n"); 58 } 59 60 body.append("}"); 61 //替换拦截器方法的主体内容,并将该方法添加到class之中 62 newCtMethod.setBody(body.toString()); 63 cct.addMethod(newCtMethod); 64 65 //输出拦截器的代码块 66 System.out.println("拦截器方法的主体:"); 67 System.out.println(body.toString()); 68 } 69 }
View Code
▲1' body.append()插入字符串,里面若是输出语句需要注意用转义字符 “ “ 改成/“ /"
2’ 构造一个addtiming(0方法,方法里构造拦截器时使用一个 java.lang.StringBuffer
来累积正文文本(这显示了处理 String
的构造的正确方法。这种变化取决于原来的方法是否有返回值。如果它 有返回值,那么构造的代码就将这个值保存在局部变量中,这样在拦截器方法结束时就可以返回它。如果原来的方法类型为 void
,那么就什么也不需要保存,也不用在拦截器方法中返回任何内容。
3‘ body.append(nname + "($$);\n")
中 nname
是原来方法修改后的名字。在调用中使用的 $$
标识符是 Javassist 表示正在构造的方法的一系列参数的方式。通过在对原来方法的调用中使用这个标识符,在调用拦截器方法时提供的参数就可以传递给原来的方法。
3‘ 运行
在eclipse里运行插桩程序和被插桩程序,然后看当地目录下又生成一个Triangle.class文件,大小比原来的大一点。
在cmd代码目录下运行该文件 java Triangle
▲原Triangle里没有main方法,需自己插入main方法并创建一个对象引用第一个方法isTriangle()
比较插桩前后Triangle.class,在当地目录找到Triangle.class(后生成的,不在bin目录里,较大的文件),然后用,导入该文件,可以查看原java代码,会发现多了四个类似的方法:
1 import java.io.PrintStream; 2 3 public class Triangle 4 { 5 public static void main(String[] args) 6 { 7 Triangle tr = new Triangle(2L, 3L, 4L); 8 tr.isTriangle(tr); 9 } 10 11 protected long lborderA = 0L; 12 protected long lborderB = 0L; 13 protected long lborderC = 0L; 14 15 public Triangle(long lborderA, long lborderB, long lborderC) 16 { 17 this.lborderA = lborderA; 18 19 this.lborderB = lborderB; 20 21 this.lborderC = lborderC; 22 } 23 24 public boolean isTriangle$impl(Triangle triangle) 25 { 26 boolean isTriangle = false; 27 if ((triangle.lborderA > 0L) && (triangle.lborderA <= 9223372036854775807L) && 28 (triangle.lborderB > 0L) && (triangle.lborderB <= 9223372036854775807L) && 29 (triangle.lborderC > 0L) && (triangle.lborderC <= 9223372036854775807L)) { 30 if ((diffOfBorders(triangle.lborderA, triangle.lborderB) < triangle.lborderC) && 31 (diffOfBorders(triangle.lborderB, triangle.lborderC) < triangle.lborderA) && 32 (diffOfBorders(triangle.lborderC, triangle.lborderA) < triangle.lborderB)) { 33 isTriangle = true; 34 } 35 } 36 return isTriangle; 37 } 38 39 public String getType$impl(Triangle triangle) 40 { 41 String strType = "Illegal"; 42 if (isTriangle(triangle)) { 43 if ((triangle.lborderA == triangle.lborderB) && 44 (triangle.lborderB == triangle.lborderC)) { 45 strType = "Regular"; 46 } else if ((triangle.lborderA != triangle.lborderB) && 47 (triangle.lborderB != triangle.lborderC) && 48 (triangle.lborderA != triangle.lborderC)) { 49 strType = "Scalene"; 50 } else { 51 strType = "Isosceles"; 52 } 53 } 54 return strType; 55 } 56 57 public long diffOfBorders$impl(long a, long b) 58 { 59 return a > b ? a - b : b - a; 60 } 61 62 public long[] getBorders$impl() 63 { 64 long[] borders = new long[3]; 65 borders[0] = this.lborderA; 66 borders[1] = this.lborderB; 67 borders[2] = this.lborderC; 68 return borders; 69 } 70 71 public boolean isTriangle(Triangle paramTriangle) 72 { 73 long l = System.currentTimeMillis(); 74 System.out.println(" isTriangle$impl_in_11"); 75 boolean bool = isTriangle$impl(paramTriangle); 76 System.out.println(" isTriangle$impl_out_11"); 77 return bool; 78 } 79 80 public String getType(Triangle paramTriangle) 81 { 82 long l = System.currentTimeMillis(); 83 System.out.println(" getType$impl_in_11"); 84 String str = getType$impl(paramTriangle); 85 System.out.println(" getType$impl_out_11"); 86 return str; 87 } 88 89 public long diffOfBorders(long paramLong1, long paramLong2) 90 { 91 long l1 = System.currentTimeMillis(); 92 System.out.println(" diffOfBorders$impl_in_11"); 93 long l2 = diffOfBorders$impl(paramLong1, paramLong2); 94 System.out.println(" diffOfBorders$impl_out_11"); 95 return l2; 96 } 97 98 public long[] getBorders() 99 { 100 long l = System.currentTimeMillis(); 101 System.out.println(" getBorders$impl_in_11"); 102 long[] arrayOfLong = getBorders$impl(); 103 System.out.println(" getBorders$impl_out_11"); 104 return arrayOfLong; 105 } 106 }
View Code
总结:
Javassist 不仅是一个处理字节码的库,而且更因为它的另一项功能使得它成为试验 classworking 的很好的起点。这一项功能就是:可以用 Javassist 改变 Java 类的字节码,而无需真正了解关于字节码或者 Java 虚拟机(Java virtual machine JVM)结构的任何内容。
用Javassist API的方法,对插桩程序用ctclass、classpool、CtMethod等方法进行方法插桩,然后生成第二个插桩程序的class文件,此时run这个class文件就会显示插入的语句要输出的内容。
转载于:https://www.cnblogs.com/1996swg/p/7157205.html
Javassist进行方法插桩相关推荐
- Java ASM框架与字节码插桩的常见用法(生成类,修改类,方法插桩,方法注入)
前言 ASM 是一款读写Java字节码的工具,可以达到跳过源码编写,编译,直接以字节码的形式创建类,修改已经存在类(或者jar中的class)的属性,方法等. 通常用来开发一些Java开发的辅助框架, ...
- 字节码插桩(javassist)之插入代码块|IOC框架(Hilt)之对象注入~研究
Hilt对象注入 | javassist插桩 研究 Hilt对象注入 javassist字节码插桩 创建buildSrc的module 重写Transform 熟悉TransformInvocatio ...
- 调研字节码插桩技术,用于系统监控设计和实现
作者:小傅哥 博客:https://bugstack.cn ❝ 沉淀.分享.成长,让自己和他人都能有所收获!???? ❞ 目录 一.来自深夜的电话! 二.准备工作 三.使用 AOP 做个切面监控 1. ...
- Android程序员的硬通货——ASM字节码插桩
作者:享学课堂Lance老师 转载请声明出处! 一.什么是插桩 QQ空间曾经发布的<热修复解决方案>中利用 Javaassist库实现向类的构造函数中插入一段代码解决 CLASS_ISPR ...
- 字节码插桩框架ASM(一)
本文大纲: ams是做什么的 asm使用 1.ASM是做什么的? 简单来说,asm是用来进行字节码插桩的.什么是字节码插桩? 字节码插桩就是修改节码文件(.class). 如同 gson框架是用来做操 ...
- JVM插桩之三:javaagent介绍及javassist介绍
本文介绍一下,当下比较基础但是使用场景却很多的一种技术,稍微偏底层点,就是字节码插桩技术了...,如果之前大家熟悉了asm,cglib以及javassit等技术,那么下面说的就很简单了...,因为下面 ...
- aop 获取方法入参出参_ASM字节码编程 | JavaAgent+ASM字节码插桩采集方法名称及入参和出参结果并记录方法耗时...
作者:小傅哥 博客:bugstack.cn ❝ 沉淀.分享.成长,让自己和他人都能有所收获! ❞ 一.前言 在我们实际的业务开发到上线的过程中,中间都会经过测试.那么怎么来保证测试质量呢?比如:提交了 ...
- ASM字节码编程 | JavaAgent+ASM字节码插桩采集方法名称以及入参和出参结果并记录方法耗时
作者:小傅哥 博客:bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 在我们实际的业务开发到上线的过程中,中间都会经过测试.那么怎么来保证测试质量呢?比如:提交了多少代码 ...
- Android AOP之字节码插桩
背景 本篇文章基于<网易乐得无埋点数据收集SDK>总结而成,关于网易乐得无埋点数据采集SDK的功能介绍以及技术总结后续会有文章进行阐述,本篇单讲SDK中用到的Android端AOP的实 ...
- 介绍自己的一个Android插桩热修复框架项目QuickPatch
QuickPatch项目地址: https://gitee.com/egg90/QuickPatch 和 https://github.com/eggfly/QuickPatch 同步更新 类似于美团 ...
最新文章
- Hadoop + HBase (自带zookeeper 也可单独加) 集群部署
- stitching detail输出的dot图含义
- 吞吐量达到瓶颈后下降_如何找到 Kafka 集群的吞吐量极限?
- erlang分布式编程模型
- Perl中判断数组或hash为空
- 文件系统03 - 零基础入门学习Delphi36
- html网页自动提示框代码,多种网页弹出窗口代码
- 网站备案后服务器更换到家里,域名备案后能更换服务器吗
- 国庆假期都干了些啥?
- 测绘专业计算机知识,测绘科学技术的发展趋势
- 用python实现双人五子棋(终端版)
- hapi入门简介(入门实践)----净土小沙弥学hapi.js_第二篇
- 如何从股市曲线看出买进卖出的最佳时期?利用Python和分治策略轻松搞定
- adb命令——adb shell top命令
- Java_持久层_MyBatis
- 蚂蚁金服Java岗社招面试5面历程
- oracle日期导出mysql_Oracle 获取系统日期时间,导出数据库
- ffmpeg绿幕抠图原理解析
- 用户读书喜好推荐数据分析
- 【Verilog】一、Verilog的always使用
热门文章
- 逻辑运算符 用法解释
- apache 网页301重定向、自定义400/403/404/500错误页面
- 如何在一台电脑上使用两个git@osc的账号进行操作
- 旧文重现,10种职场经典寓言
- linux远程桌面速度,【图片】linux下哪种远程桌面服务最快?_linux吧_百度贴吧
- java读加密脚本_尝试将wlst脚本嵌入到java类中时发生加密错误
- php聊天系统文档,聊天后台管理系统接口文档
- 华中师范大学计算机学院郑,郑年亨-华中师范国家数字化研究中心
- python反向查找字符_序列化使用(正向和反向查找)
- php显示网络图片,PHP在服务器端怎么显示图片呢?