JAVA獲取方法參數名的分析(一)


關於題目

首先解釋一下題目. 我們知道, Java通過反射,可以從一個類得知它有哪些方法,有哪些變量,也可以知道每個方法中有哪幾個什么類型的傳入參數。但有一個東西反射取不到,那就是我們對方法傳入參數的命名。

 

取得傳入參數的名字有什么意義?

對這個問題的探究,源於在寫一個測試類時候的需求。假設我們有一個類需要測試,這個類中有數十個方法。為每個方法編寫測試類,將耗費大量的時間和精力。因此我有一種想法,就是通過java的反射,獲得這個類所有的方法,再通過傳入參數的名字和參數類型,來生成一些符合要求的數據進行傳入。(能這樣生成數據的前提是:這個類的編碼需要遵循嚴格的規范,對參數的命名有統一的標准,同時,這個類應該和某種業務緊密相關,這樣,才能通過業務和參數名字,判斷應生成什么合適的數據)。如果能做到上面說的,那么對具有數十或數百個方法的類,要測試的話只需要傳入這個類就可以了。

 

存在的問題

根據上面的設想,問題就出現了。獲得類的方法,獲得類的參數類型,反射都可以做到。但參數名稱呢?上網求證,多數人給了直接否定的答案。因為API中根本沒有提供相關的方法。但有一些人的觀點啟發了我。他們提到,IDE(如eclipse,myeclipse)中在編碼過程中,調用一個類的方法,在代碼提示的時候,ide是可以顯示出方法中的參數名字的,如下圖:


IDE是怎樣做到的呢,如果IDE可以做到,我們是否可以嘗試去分析它們的做法,來獲得參數名稱。

 

 

可能的做法

網上找到了一個很直觀的方法——通過直接讀取.java文件,把類作為一個普通文本,用正則表達式匹配方法,來直接獲取參數的名字。

 

Java代碼   收藏代碼
  1. /**    
  2.  *   @author   zhangc    
  3.  *    
  4.  *一個測試程序,用來掃描文件(java文件),找出所有方法的參數列表    
  5.  */  
  6. import java.io.*;  
  7. import java.util.regex.*;  
  8.   
  9. public class ScanSource {  
  10.     static void findArgsList(Object targetSrc) {  
  11.         /* 
  12.          * 正則匹配串基本上是這樣子分組情況(A(B(c(d)))) 
  13.          * 串是:(\\w+\\s+\\w+\\s*\\(((\\s*\\w+\\s*(\\[ 
  14.          * \\])*\\s*\\s+(\\[\\])*\\s*\\w+\\s*(\\[\\])*,?)+)\\)\\s*\\{) 比如public 
  15.          * static void findArgsList(Object targetSrc,int []a){ 
  16.          * A是匹配整個方法定義行:這里是:static void findArgsList(Object targetSrc,int []a){ 
  17.          * B是匹配匹配參數列表:這里是Object targetSrc,int []a 
  18.          * C是匹配一個參數,包括類型和類型名稱和逗號:這里是Object targetSrc, D是匹配數組標識符:這里是[] 
  19.          * 這個串有點bt,水平有限,只能這樣 
  20.          */  
  21.         Pattern p = Pattern  
  22.                 .compile("(\\w+\\s+\\w+\\s*\\(((\\s*\\w+\\s*(\\[\\])*\\s*\\s+(\\[\\])*\\s*\\w+\\s*(\\[\\])*,?)+)\\)\\s*\\{)");  
  23.         Matcher m = p.matcher((CharSequence) targetSrc);  
  24.   
  25.         // locate the all methord defination  
  26.         while (m.find()) {  
  27.             String methodName = m.group(0);  
  28.             String methodArgName = m.group(1);  
  29.             String strArgs = m.group(2);  
  30.             String fourArgs = m.group(3);  
  31.             System.out.println(methodName + "\n" + methodArgName + "\n" + strArgs + "\n" + fourArgs + "\n");  
  32.         }  
  33.   
  34.     }  
  35.   
  36.     public static String LoadTargetFile(String targetFileName) {  
  37.         String result = null;  
  38.         try {  
  39.             FileInputStream fis = new FileInputStream(targetFileName);  
  40.   
  41.             // 臨時分配10000size給byte數組。  
  42.             byte[] bufReceived = new byte[10000];  
  43.   
  44.             int counts = fis.read(bufReceived);  
  45.             byte[] bufActual = new byte[counts];  
  46.             System.arraycopy(bufReceived, 0, bufActual, 0, counts);  
  47.             result = new String(bufActual);  
  48.         } catch (FileNotFoundException e) {  
  49.             e.printStackTrace();  
  50.         } catch (IOException e) {  
  51.             e.printStackTrace();  
  52.         }  
  53.         return result;  
  54.     }  
  55.   
  56.     public static void main(String[] args) {  
  57.         String target = LoadTargetFile("src/com/spring/aop/TestAspect.java");  
  58.         System.out.println(target);  
  59.         findArgsList(target);  
  60.     }  
  61. }  

 

 

 

 這個通過正則表達式的類,在我寫的一個簡單的測試類中,是可以取得參數的值的,但當把它用在我們那個有幾十個方法的類的時候,表達式的匹配就失效了,沒有得到任何的結果(具體原因可能是正則表達式的錯誤,沒能匹配到一些方法)。同時,這種方法需要有.java這個源文件,而在IDE中引入的常常是.class組成的Jar包。為了進一步了解IDE對方法傳入參數名的處理,下面我做了一個測試。

 

 

測試IDE對方法傳入參數的處理

建立一個工程。在工程中新建如下的一個類:

 

 

Java代碼   收藏代碼
  1. package testplugin;  
  2.   
  3. public class TestJar {  
  4.     public void testJar(String jarName, String yourName){  
  5.         System.out.println("jarName:" + jarName + "|| yourName:" + yourName);  
  6.     }  
  7. }  

 

接着我們用2種方式對這個類打jar包:

 

1. 用javac編譯類文件然后打到jar包中,命名為testPlugin_javac.jar.

2. 用MyEclipse直接對工程進行導出,導出為testPlugin_myeclipse.jar.

 

(打開2個jar中的TestJar.class文件,會發現2個class文件有差異)。

 

再建立一個工程,先后將2個jar包引入做實驗,可以看到:

 

1. 引入testPlugin_javac.jar, 調用testJar方法,如下圖



 可以看到,2個傳入參數失去了原有的名稱。

 

2. 移除上面的包,引入testPlugin_myEclipse.jar, 調用testJar方法,如下圖



 可以看到,參數名稱被識別出來了。

 

關鍵在於,2個jar包中的class文件不同。我們打開2個class文件(我們只是直觀的看一下class文件中的變量,所以沒有用專用的工具查看):

javac生成的.class:

 

 

myelipse直接打出來的.class(實際上就是調用了debug模式編譯出來的.class):

 

2個class文件里下面的部分都有SourceFile塊。應該是用來表示這個class文件是從哪個java文件編譯來的。我們重點看上面的部分。

 

可以看到,用普通的javac編譯出來的類,方法的傳入參數名會被編譯器修改,於是上面第一個圖里SourceFile以上的部分就找不到jarName和yourName 2個名字。而只有通過-debug模式編譯出來的類,它的參數名才能被保存下來。而也就是在.class文件中有保留下來參數名的jar包,在IDE中代碼提示才能正確顯示出參數名字。

 

那么說明IDE是否能識別類中的方法名取決於編譯過后產生的不同的class文件。那么下一節我們會使用工具來解析這2個class文件來看其中到底有什么不同。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM