1. 程式人生 > >[Java]Javassist基本用法

[Java]Javassist基本用法

Javassist是一個能夠操作位元組碼框架,在學習的過程中存在了一些問題,用部落格的方式記錄下來,希望對大家有所幫助。

一、例項功能

    學習的例項來自於 IBM developer   主要功能實現計算一個方式具體的執行時間. 

二、程式碼例項

  1. package org.java.javassist.one;  
  2. /** 
  3.  * 該類並不是對StringBuilder進行解釋,而是提供了中方式,方便我們來使用javassist的一些細節 
  4.  * @author xianglj 
  5.  * @date 2016/7/13 
  6.  * @time 9:59 
  7.  */
  8. publicclass StringBuilderTest {  
  9.     /** 
  10.      * 假如我們現在需要計算該程式的計算時間: 
  11.      * 則可以標記開始時間(start)和結束時間(end) 
  12.      * 最終的執行時間為(end - start)的值 
  13.      * @param length 
  14.      * @return 
  15.      */
  16.     public String buildString(int length) {  
  17.         String result = "";  
  18.         for(int i = 0; i<length; i++ ) {  
  19.             result += (i %26 + 
    'a');  
  20.         }  
  21.         return result;  
  22.     }  
  23.     publicstaticvoid main(String[] args) {  
  24.         /** 
  25.          * class.getName()返回的字串中,不僅包括了類的名稱,同時也包含了該類所在的包名稱 
  26.          * <pre> 
  27.          *     格式: 
  28.          *     packagename.classname 
  29.          * </pre> 
  30.          */
  31. //        System.out.println(StringBuilderTest.class.getName());
  32.         StringBuilderTest test = new StringBuilderTest();  
  33.         if(null != args) {  
  34.             for(int i = 0, len = args.length; i<len; i++) {  
  35.                 String result = test.buildString(Integer.parseInt(args[i]));  
  36.                 System.out.println("result:" + result);  
  37.             }  
  38.         }  
  39.     }  
  40. }  
這是一個基本例項,通過一個引數傳入的length,來生成length長度的字串。(但是該方式存在一個很驗證的效能問題,就是當length的長度元件增大時,該方法的效率就會越低),但在此處不必關係方法的效率問題

三、解決方案

   1) 第一個中解決方案,也是最直觀的方式,就是在進入方法時,記錄一個當前時間 start , 當代碼執行完成之後,獲取當前時間 end , 然後採用 (end - start)的方式,來獲取程式碼執行時間

    2) 第二中方式,我們採用Javassit框架來實現:

        (1)  使用通過ClassPool 來獲取 CtClass物件

        (2)  從CtClass物件中,獲取buildString()的方法

        (3)  為buildString()方法新增程式碼塊

特別說明:

         為方法新增程式碼塊,有三種方式可以實現

  1. * <pre>  
  2. *     1. 新增的程式碼不能引用在方法中其他地方定義的區域性變數。這種限制使我不能在 Javassist 中使用在原始碼中使用的同樣方法實現計時程式碼,  
  3. *        在這種情況下,我在開始時新增的程式碼中定義了一個新的區域性變數,並在結束處新增的程式碼中引用這個變數。  
  4. *  
  5. *     2. 我 可以在類中新增一個新的成員欄位,並使用這個欄位而不是區域性變數。不過,這是一種糟糕的解決方案,  
  6. *        在一般性的使用中有一些限制。例如,考慮在一個遞迴方法中會發生的事情。每次方法呼叫自身時,上次儲存的開始時間值就會被覆蓋並且丟失。  
  7. *  
  8. *     3. 我可以保持原來方法的程式碼不變,只改變方法名,然後用原來的方法名增加一個新方法。  
  9. * </pre>  
前兩種方式,在實現上都有一定的問題,所以我們採用第三種方式實現,會比較的容易實現

程式碼如下:

  1. package org.java.javassist.one;  
  2. import javassist.*;  
  3. import java.io.IOException;  
  4. /** 
  5.  * 通過Javassist來為需要實現計算的方法前後各加上一個攔截器, 
  6.  * 依次來實現方法計算的時間 
  7.  * <pre> 
  8.  *     1. 新增的程式碼不能引用在方法中其他地方定義的區域性變數。這種限制使我不能在 Javassist 中使用在原始碼中使用的同樣方法實現計時程式碼, 
  9.  *        在這種情況下,我在開始時新增的程式碼中定義了一個新的區域性變數,並在結束處新增的程式碼中引用這個變數。 
  10.  * 
  11.  *     2. 我 可以在類中新增一個新的成員欄位,並使用這個欄位而不是區域性變數。不過,這是一種糟糕的解決方案, 
  12.  *        在一般性的使用中有一些限制。例如,考慮在一個遞迴方法中會發生的事情。每次方法呼叫自身時,上次儲存的開始時間值就會被覆蓋並且丟失。 
  13.  * 
  14.  *     3. 我可以保持原來方法的程式碼不變,只改變方法名,然後用原來的方法名增加一個新方法。 
  15.  * </pre> 
  16.  * @author xianglj 
  17.  * @date 2016/7/13 
  18.  * @time 10:07 
  19.  */
  20. publicclass JavassistTiming {  
  21.     publicstaticvoid main(String[] args) {  
  22.         //開始獲取class的檔案
  23.         javassist();  
  24.     }  
  25.     publicstaticvoid javassist() {  
  26.         //開始獲取class的檔案
  27.         try {  
  28. //            String classFileName = StringBuilderTest.class.getName();
  29.             String classFileName = "org.java.javassist.one.StringBuilderTest";  
  30.             CtClass ctClass = ClassPool.getDefault().getCtClass(classFileName);  
  31.             if(ctClass == null) {  
  32.                 System.out.println("Class File (" + classFileName + ") Not Found.....");  
  33.             } else {  
  34.                 addTiming(ctClass, "buildString");  
  35.                 //為class新增計算時間的過濾器
  36.                 ctClass.writeFile();  
  37.             }  
  38.             Class<?> clazz = ctClass.toClass();  
  39.             StringBuilderTest test = (StringBuilderTest) clazz.newInstance();  
  40.             test.buildString(2000);  
  41.         } catch (NotFoundException e) { //類檔案沒有找到
  42.             e.printStackTrace();  
  43.         } catch (CannotCompileException e) {  
  44.             e.printStackTrace();  
  45.         } catch (IOException e) {  
  46.             e.printStackTrace();  
  47.         } catch (InstantiationException e) {  
  48.             e.printStackTrace();  
  49.         } catch (IllegalAccessException e) {  
  50.             e.printStackTrace();  
  51.         }  
  52.     }  
  53.     /** 
  54.      * 為方法新增攔截器: 
  55.      * <pre> 
  56.      *     構造攔截器方法的正文時使用一個 java.lang.StringBuffer 來累積正文文字(這顯示了處理 String 的構造的正確方法, 
  57.      *     與在 StringBuilder 的構造中使用的方法是相對的)。這種變化取決於原來的方法是否有返回值。 
  58.      *     如果它 有返回值,那麼構造的程式碼就將這個值儲存在區域性變數中,這樣在攔截器方法結束時就可以返回它。 
  59.      *     如果原來的方法型別為 void ,那麼就什麼也不需要儲存,也不用在攔截器方法中返回任何內容。 
  60.      * </pre> 
  61.      * @param clazz 方法所屬的類 
  62.      * @param method 方法名稱 
  63.      */
  64.     privatestaticvoid addTiming(CtClass clazz, String method) throws NotFoundException, CannotCompileException {  
  65.         //獲取方法資訊,如果方法不存在,則丟擲異常
  66.         CtMethod ctMethod = clazz.getDeclaredMethod(method);  
  67.         //將舊的方法名稱進行重新命名,並生成一個方法的副本,該副本方法採用了過濾器的方式
  68.         String nname = method + "$impl";  
  69.         ctMethod.setName(nname);  
  70.         CtMethod newCtMethod = CtNewMethod.copy(ctMethod, method, clazz, null);  
  71.         /* 
  72.          * 為該方法新增時間過濾器,用來計算時間。 
  73.          * 這就需要我們去判斷獲取時間的方法是否具有返回值 
  74.          */
  75.         String type = ctMethod.getReturnType().getName();  
  76.         StringBuffer body = new StringBuffer();  
  77.         body.append("{\n long start = System.currentTimeMillis();\n");  
  78.         if(!"void".equals(type)) {  
  79.             body.append(type + " result = ");  
  80.         }  
  81.         //可以通過$$將傳遞給攔截器的引數,傳遞給原來的方法
  82.         body.append(nname + "($$);\n");  
  83.         //  finish body text generation with call to print the timing
  84.         //  information, and return saved value (if not void)
  85.         body.append("System.out.println(\"Call to method " + nname + " took \" + \n (System.currentTimeMillis()-start) + " +  "\" ms.\");\n"

    相關推薦

    [Java]Javassist基本用法

    Javassist是一個能夠操作位元組碼框架,在學習的過程中存在了一些問題,用部落格的方式記錄下來,希望對大家有所幫助。 一、例項功能     學習的例項來自於 IBM developer   主要功能實現計算一個方式具體的執行時間.  二、程式碼例項 p

    java基礎:運算符的基本用法

    自增 另一個 stat 不同 兩個 流程 ava -- 個數 運算符: 就是對常量和變量進行操作的符號。 算數運算符: A:+,-,*,/,%,++,-- B:+的用法 a:加法 b:正號 c:字符串連接符 C:/和%的區別 數據做除法操作的時候,/取得是商

    Java從入門到放棄》入門篇:springMVC基本用法

    java springmvc springMVC可以理解成用來做數據顯示處理的框架,主要內容就是控制器和視圖的處理。在已經安裝了spring框架的基礎上繼續下面的步驟(我使用的MyEclipse2014)。 1. 修改web.xml文件 2. 在WEB-INF目錄創建springmvc的配

    java中正則表達式基本用法(轉)

    code ack acea print 表達式 劃線 跟著 以及 n) https://www.cnblogs.com/xhj123/p/6032683.html 正則表達式是一種可以用於模式匹配和替換的規範,一個正則表達式就是由普通的字符(例如字符a到z)以及特殊字符(元

    JAVA--properties的基本用法

    java中的properties檔案是一種配置檔案,主要用於表達配置資訊,檔案型別為*.properties,格式為文字檔案,檔案的內容是格式是"鍵=值"的格式,在properties檔案中,可以用"#"來作註釋,properties檔案在Java程式設計中用到的地方很多,操作很方便。一、prope

    java註解之編譯時註解RetentionPolicy.CLASS 基本用法

    1 前言 我們知道,在日常開發中我們常用的兩種註解是執行時註解和編譯時註解,執行時註解是通過反射來實現註解處理器的,對效能稍微有一點損耗,而編譯時註解是在程式編譯期間生成相應的代理類,替我們完成某些功能。今天我們來講解一下編譯時註解以及寫一個小例子,以便加深對編譯時註解的理解。

    Java String類的基本用法

    一、String類物件的建立 字串宣告:String stringName; 字串建立:stringName = new String(字串常量);或stringName = 字串常量; 二、String類構造方法 1、public String() 無參構造方法,用來建立空字串的Strin

    知識困惑丨java中return的基本用法

    今天做一個題,在語句中使用了return,怎麼都得不到自己想要的結果,後來,把return去掉,換了輸出語句,所以就瞭解下return的用法; 第一個用法:方法中定義了資料型別,則必須要有一個返回值用return; public int Return() { return 0;

    Java BufferedImage的基本用法

    1:讀取本地圖片:        File file = new File(”001.jpg“);//本地圖片        BufferedImage image=(

    Java string的基本用法

    一,定義字串與子串 定義 String e ="";\空字串 String E=“Hello”; 提取子串使用Substring方法: String E=“Hello”; String s=E.substring(0,4);\s等於Hell System.out.println(s)

    Java string特點及基本用法

    開始學習Java之後,便會接觸到string類。string就像是C語言中常說的字串,字串其實就是一個String類的物件。 在這裡我們簡單介紹一下string類的用法特點,並且結合程式碼演示。 1、定義字串 第一步我們先定義一下字串。字串的定義一般有兩種方法,

    Java中的getGenericSuperclass方法的基本用法

    通過getGenericSuperclass方法可以獲取當前物件的直接超類的 Type package cn.tzz.lang.clazz; public class User { privat

    java 執行緒池基本用法

    1,執行緒與程序的基本區別 ->程序: 擁有自己一整套變數,資料之間不可見。建立/撤銷-開銷很大。 ->執行緒: 共享資料,共享變數儲存在主存中(Main Memory),每個現場有自己私有的本地記憶體(Local Memory),本地記憶體存的是主存的一個副本,與程

    Java產生隨機數用法基本用法(轉)

    1.隨機產生四位數[1000,9999] num=(int)(Math.random()*9000)+1000; Math.random()方法是產生double型[0,1)的資料,[0,1)*9000=[1,9001),用int型別強轉後便是[0,8999], 因而可以得到1000~9

    java中ArrayList用法詳解,基本用法(含增刪改查)

    1、什麼是ArrayList ArrayList就是動態陣列,它提供了①動態的增加和減少元素 ②實現了ICollection和IList介面 ③靈活的設定陣列的大小ArrayList是一個其容量能夠動態增長的動態陣列。它繼承了AbstractList,實現了List、Rand

    JAVA中Set的基本用法

      首先我們來介紹常見的Set型別:HashSet它有幾個特性,首先它不會出現重複的元素,其次它是無序的,此外它可以含有空元素。下面我們看示例:package lab1;import java.util.HashSet;import java.util.Iterator;im

    JDBC(Java Data Base Connectivity)基本用法

    一、什麼是JDBC JDBC(Java Database Connection)為java開發者使用資料庫提供了統一的程式設計介面,它由一組java類和介面組成.是java程式與資料庫系統通訊的標準A

    Java學習筆記之LinkedList基本用法

    LinkedList簡介LinkedList 是一個繼承於AbstractSequentialList的雙向連結串列。它也可以被當作堆疊、佇列或雙端佇列進行操作。LinkedList 實現 List 介面,能進行佇列操作。LinkedList 實現 Deque 介面,即能將L

    java中的string類的基本用法

    內容:介紹String類如何判斷是否相等,字串的子串,如何連線字串等等,並給出相應的程式碼。(要求:不少於1000字,Java程式碼不少於100行。) ①string類如何判斷是否相等 ava中的是用來判斷物件所使用的記憶體地址是不是同一個,進而判斷是不是同一個

    作業2 Java String類的基本用法

    字串型別屬於引用資料型別,Java中用String表示字串型別。 string是final所修飾的。代表著string這個類不能有子類。(也就是指類中對字串操作的功能是不能被我們複寫)String類物件建立後不能修改,由0或多個字元組成,包含在一對雙引號之間。