zoukankan      html  css  js  c++  java
  • 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("{
     long start = System.currentTimeMillis();
    ");  
    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 + "($$);
    ");  
    53        //输出方法运行时间差
    54       //  body.append("System.out.println("Call to method " + nname + " took " + 
     (System.currentTimeMillis()-start) + " +  "" ms.");
    ");  
    55         body.append("System.out.println(" "+ nname +"_out_11");");
    56         if(!"void".equals(type)) {  
    57             body.append("return result;
    ");  
    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 + "($$); ") 中 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文件就会显示插入的语句要输出的内容。

     

     

     

     
  • 相关阅读:
    [USACO 2012 Feb B]Moo
    [Atcoder ARC124] XOR Matching 2-小思维 | 暴力
    loj数列分块入门
    2019牛客暑期多校2-Partition problem深搜
    Codeforces 1554C
    [USACO 2012 Feb G]Cow Coupons----贪心&带悔(看完稳AC)
    Codeforces 220B-Little Elephant and Array-扫描线 & 树状数组
    [AtCoder ARC098] Donation| 建图 | 树型dp
    关于幂等性以及怎么实现幂等性
    【OOM】解决思路
  • 原文地址:https://www.cnblogs.com/1996swg/p/7157205.html
Copyright © 2011-2022 走看看