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);   
相關文章
相關標籤/搜索