java asm 框架 浅析

  1. 什么是asm呢?asm是assembly的缩写,是汇编的称号,对于java而言,asm就是字节码级别的编程。  
  2. 而这里说到的asm是指objectweb asm,一种.class的代码生成器的开源项目.  
  3. ASM是一套java字节码生成架构,它可以动态生成二进制格式的stub类或其它代理类,  
  4. 或者在类被java虚拟机装入内存之前,动态修改类。  
  5. 现在挺多流行的框架都使用到了asm.所以从aop追溯来到了这。  
  6.   
  7. 1.什么是ObjectWeb ASM  
  8.     ObjectWeb ASM是轻量级的Java字节码处理框架。它可以动态生成二进制格式的stub类或其他代理类,或者在类被JAVA虚拟机装入内存之前,动态修改类。   
  9.     ASM 提供了与 BCEL和SERP相似的功能,只有22K的大小,比起350K的BCEL和150K的SERP来说,是相当小巧的,并且它有更高的执行效率,  
  10.     是BCEL 的7倍,SERP的11倍以上。  
  11.   
  12. 在我看来,ObjectWeb ASM具有如下几个非常诱人的特点  
  13.     * 小巧、高效  
  14.     * 源代码实现非常简洁而又优雅,简直就是Gof的《设计模式》非常棒的注解  
  15.     * 字节码级的控制,能够更高效地实现字节码的控制  
  16.   
  17. ObjectWeb ASM有2组接口:  
  18.     * 基于事件驱动的接口,类似于xml的SAX接口,visitor模式,在访问到类定义某个部分的时候进行回调,实现上比tree接口高效,占用内存更小  
  19.     * 基于tree的接口,类似于xml的DOM接口,将类定义解析成tree  
  20.   
  21. 这里我们将使用ObjectWeb ASM的事件驱动接口  
  22.   
  23. 2. 目标  
  24.     我们将对已有的字节码进行增强,收集进入方法和退出方法的信息,这里主要解决Method Monitor的字节码增强部分,  
  25.     不对收集后的数据处理做更深入地研究,出于演示的目的,我们定义了如下的收集方法的访问信息处理,  
  26.     在实际应用中,我们可能会使用更好的格式收集更多的数据、使用异步处理提高性能、使用批量处理提高处理能力、使用友好的UI显示信息等等,  
  27.     此处不对这部分进行探讨  
  28.   
  29.    1. package blackstar.methodmonitor.instrutment.monitor;    
  30.    2. public class MonitorUtil    
  31.    3. {    
  32.    4.     public final static String CLASS_NAME = MonitorUtil.class.getName()    
  33.    5.             .replaceAll("\\.", "/");    
  34.    6.     public final static String ENTRY_METHOD = "entryMethod";    
  35.    7.     public final static String EXIT_METHOD = "exitMethod";    
  36.    8.     public final static String METHOD = "(Ljava/lang/String;Ljava/lang/String;)V";    
  37.    9.     
  38.   10.     public static void entryMethod(String className, String methodName)    
  39.   11.     {    
  40.   12.         System.out.println("entry : " + className + "." + methodName);    
  41.   13.     }    
  42.   14.     
  43.   15.     public static void exitMethod(String className, String methodName)    
  44.   16.     {    
  45.   17.         System.out.println("exit : " + className + "." + methodName);    
  46.   18.     }    
  47.   19. }    
  48.   
  49. 3. 从字节码开始  
  50. 实际上,对于被监控制的代码,我们所需要实现的功能如下,红色部分的代码是我们需要在动态期插到字节码中间的  
  51.   
  52. public xxx method(…)  
  53. {  
  54.     try  
  55.     {  
  56.          methodEntry(…)  
  57.   
  58.          methodCode  
  59.      }  
  60.       finally  
  61.      {  
  62.           methodExit(…)  
  63.      }  
  64. }   
  65.   
  66. 这个问题看起来简单,实际则没有那么容易,因为在JVM的字节码设计中,字节码并不直接支持finally语句,而是使用try…catch来模拟的,我们先来看一个例子  
  67.   
  68. Java代码  
  69.   
  70.    1. package blackstar.methodmonitor.instrutment.test;    
  71.    2.     
  72.    3. public class Test    
  73.    4. {    
  74.    5.     public void sayHello() throws Exception    
  75.    6.     {    
  76.    7.         try    
  77.    8.         {    
  78.    9.             System.out.println("hi");    
  79.   10.         } catch (Exception e)    
  80.   11.         {    
  81.   12.             System.out.println("exception");    
  82.   13.             return;    
  83.   14.         } finally    
  84.   15.         {    
  85.   16.             System.out.println("finally");    
  86.   17.         }    
  87.   18.     }    
  88.   19. }    
  89.   
  90. 我们看看字节码是如何处理finally语句的  
  91.       首先看看异常表,异常是在JVM级别上直接支持的,下面异常表的意思是,在执行0-8语句的时候,如果有异常java.lang.Exception抛出,则进入第11语句,  
  92.     在执行0-20语句的时候,有任何异常抛出,都进入29语句。实际上JVM是这样实现finally语句的:  
  93.   
  94.     * 在任何return语句之前,都会增加finally语句中的字节码  
  95.     * 定义一个捕获所有异常的语句,增加finally语句中的字节码,如果finally中没有return语句,则会将异常再次抛出去(处理方法以抛出异常的方式结束)  
  96.   
  97. Exceptions:  
  98. [0-8): 11 - java.lang.Exception  
  99. [0-20): 29   
  100.   
  101. 我们再看看字节码具体是如何做的  
  102.   
  103. 0 getstatic java.lang.System.out  
  104. 3 ldc "hi" (java.lang.String)  
  105. 5 invokevirtual println  
  106. 8 goto 40  
  107. // System.out.println("hi");,执行完之后执行返回(goto 40)  
  108. 11 astore_1  
  109. 12 getstatic java.lang.System.out  
  110. 15 ldc "exception" (java.lang.String)  
  111. 17 invokevirtual println  
  112. // System.out.println("exception");  
  113. 20 getstatic java.lang.System.out  
  114. 23 ldc "finally" (java.lang.String)  
  115. 25 invokevirtual println  
  116. // return语句之前插入finally部分字节码  
  117. // System.out.println("finally");  
  118. 28 return  
  119. 29 astore_2  
  120. 30 getstatic java.lang.System.out  
  121. 33 ldc "finally" (java.lang.String)  
  122. 35 invokevirtual println  
  123. 38 aload_2  
  124. 39 athrow  
  125. //当在执行0-29语句中,如果有异常抛出,则执行这段finally语句  
  126. //此处的astore_2(将栈顶值——即exception的地址——设给第2个local变量)和aload_2(将第2个local变量的值入栈)这两个字节码实际是不必要的,  
  127. //但需要注意的是,如果这2段代码去掉的话,要考虑增大操作栈(max stack)以容纳这个exception地址  
  128. //System.out.println("finally");  
  129. 40 getstatic java.lang.System.out  
  130. 43 ldc "finally" (java.lang.String)  
  131. 45 invokevirtual println  
  132. // return语句之前插入finally部分字节码  
  133. // System.out.println("finally");  
  134. 48 return   
  135.   
  136. 实际上,我们需要做的就是  
  137.     * 在方法进入时插入方法进入代码(需要注意,对于构造函数不允许做这种处理,构造函数第一步必须调用父类的构造函数。  
  138.     * 在每个return操作(包括return、ireturn、freturn等)之前,插入方法退出代码  
  139.     * 定义一个捕获所有异常的处理,在处理中,插入方法退出代码(即方法以抛异常的方式终止执行)  
  140.   
  141. 4. 实现  
  142.       我们看看使用ObjectWeb ASM如何实现我们上面描述的功能  
  143.       1)ObjectWeb ASM的字节码修改  
  144.   
  145.    1. ClassReader cr = new ClassReader(byteArray); //使用字节码构监一个reader    
  146.    2. ClassWriter cw = new ClassWriter(cr, 0);//writer将基于已有的字节码进行修改    
  147.    3. MonitorClassVisitor ca = new MonitorClassVisitor(cw);//修改处理回调类    
  148.    4. cr.accept(ca, 0);   

转载于:https://my.oschina.net/chendongj/blog/778675

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值