2013-12-13 31 views
13

這是一個有點棘手的解釋。我有一個A級:如何獲取Java源代碼的完整調用層次結構?

public class A { 
    private Integer a1; 
    private Integer a2; 
    // getters and setters. 
} 

有返回我的A類靜態類B:

public static class B { 
    public static A getCurrentA() { 
     return a; 
    } 
} 

我需要找到的B返回A級的所有用途。所以我們假設C班叫c.setA(B.getCurrentA()),然後再打電話給c.getA().getA2();,我想要找到所有這些。

在真實場景中,我有217個不同的類,調用B.getCurrentA()。我無法手動關注Eclipse中的所有調用,並找出哪些方法正在調用。

Eclipse調用層次結構視圖僅顯示我對B.getCurrentA()的所有調用。

我該如何做到這一點?


編輯

克里斯·海耶斯明白我想做的事情。爲了在不破壞整個系統的情況下重構一些非常糟糕的遺留代碼,我需要首先使用Hibernate的投影來微調一些查詢(系統中的每個映射實體都被急切地加載,並且許多實體是相關的,因此一些查詢需要LONG時間提取一切)。但首先我需要找到哪些屬性使用,以便我沒有得到一個NullPointerException異常的地方......

這裏是什麼,我不得不這樣做手工的例子:

  1. 使用Eclipse的搜索,找到所有對B.getCurrentA()的調用;
  2. 打開找到的第一個方法,讓我們說這是下面的一個:

    public class CController { 
        C c = new C(); 
        CFacade facade = new CFacade(); 
        List<C> Cs = new ArrayList<C>(); 
    
        public void getAllCs() { 
         c.setA(B.getCurrentA()); // found it! 
         facade.search(c); 
        } 
    } 
    
  3. 公開賽在CFacade類的搜索方法:

    public class CFacade { 
        CBusinessObject cBo = new CBusinessObject(); 
    
        public List<C> search(C c) { 
         // doing stuff... 
         cBo.verifyA(c); 
         cBo.search(c); // yes, the system is that complicated 
        } 
    } 
    
  4. 公開賽在CBusinessObject類的verifyA方法並確定使用了字段a2:

    public class CBusinessObject { 
        public void verifyA(c) { 
         if (Integer.valueOf(1).equals(c.getA().getA2())) { 
          // do stuff 
         else { 
          // something else 
         } 
        } 
    } 
    
  5. 爲接下來的216場比賽重複步驟2-4 ...是的。

請幫忙。

+0

如果您對'B.getCurrentA()'非常具體的實例感興趣,並且您有權訪問其調用的代碼,則可以將當前堆棧跟蹤打印到日誌。或者在那裏設置一個斷點,然後運行該程序,以便在斷點處停止執行。 – FrustratedWithFormsDesigner

+1

嘗試使用'Thread.dumpStack()'來查看你方法調用的地方 – kajacx

+1

這不僅僅是調用層次結構。你想跟蹤*任何地方返回值的存儲和使用*。這聽起來像是一個靜態分析的巨大任務,試圖確定價值可能傳遞到哪裏。 –

回答

12

如果你想做任何源代碼更改/重構,你將不得不手動查找所有用法並應用你的代碼更改;

任何方式,我有兩個不同的形式給出

  1. 靜態搜索 你可以簡單地做在eclipse Text Search找到getA2()的次數。它會直接帶你到主叫方法(這裏CBusinessObject.verifyA()) - 但它會給你每getA2()occurances,可能來自不同的班級

  2. 運行時間搜索 使用java instrumentation API改變字節碼在運行時用你所需要的方法找到調用類並運行爲java agent - 使你無需觸摸現有代碼庫就能識別調用者,特別是當你無法訪問源代碼時非常有用。

在這裏你去如何實現

1步 - 寫代理主類啓動儀器

public class BasicAgent { 
       public static void premain(String agentArguments, Instrumentation instrumentation){ 
        System.out.println("Simple Agent"); 
        FindUsageTransformer transformer = new FindUsageTransformer(); 
        instrumentation.addTransformer(transformer,true); 
       } 
      } 

第2步 - 寫入一個ClassFileTransformer實施和捕捉方法

public class FindUsageTransformer implements ClassFileTransformer{ 

     Class clazz = null; 
     public byte[] transform(ClassLoader loader,String className,Class<?> classBeingRedefined, ProtectionDomain protectionDomain, 
       byte[]    classfileBuffer) throws IllegalClassFormatException { 
      if(className.equals("A")){ 
       doClass(className, classBeingRedefined, classfileBuffer); 
      } 
      return classfileBuffer; 
     } 
     private byte[] doClass(String name, Class clazz, byte[] b) { 
      ClassPool pool = ClassPool.getDefault(); 
      CtClass cl = null; 
      try { 
       cl = pool.makeClass(new java.io.ByteArrayInputStream(b)); 
       CtMethod method = cl.getDeclaredMethod("getA2"); 
       // here you have lot of options to explore 
       method.insertBefore("System.out.println(Thread.currentThread().getStackTrace()[0].getClassName()+ Thread.currentThread().getStackTrace()[0].getMethodName());"); 
       b = cl.toBytecode(); 
      } catch (Exception e) { 
       System.err.println("Could not instrument " + name 
        + ", exception : " + e.getMessage()); 
      } finally { 
       if (cl != null) { 
       cl.detach(); 
       } 
      } 
      return b; 
      } 

步驟3:創建代理類的jar文件(你必須設置與倍美力類清單文件,並添加javaassit JAR)構建文件的片段給出 - 您可以通過手動以及做

<jar destfile="build/jar/BasicAgent.jar" basedir="build/classes"> 
       <manifest> 
        <attribute name="Manifest-Version" value="1.0"/> 
        <attribute name="Premain-Class" value="com.sk.agent.basic.BasicAgent"/> 
        <attribute name="Boot-Class-Path" value="../lib/javassist.jar"/> 
       </manifest> 
      </jar> 

第4步 - 運行你用Java代理主要應用 - 該組VM參數之前加載代理

  -`javaagent:D:\softwares\AgentProject\AgentLib\build\jar\BasicAgent.jar` 

預必要條件:您需要在課程路徑中使用javassist.jar

+0

嘿,對不起,延遲!我最終在getters上使用了呼叫層次結構(Ctrl + Alt + H)。 – Tarek

2

在IntelliJ IDEA中,如果您想查找c.getA().getA2();的用法,請右鍵點擊A.a2並選擇「查找用法」。對於A.a1B.getCurrentA()也是如此。 IDEA中未使用的字段和方法以不同的顏色顯示。我聽說IntelliJ具有比Eclipse更強的重構功能,但我敢打賭,Eclipse做同樣的事情,只是略有不同。

此外,使用grep,find和sed,只需在與A相同的包中的文件或導入A的文件中搜索相應的方法,或者按名稱拼寫。

+0

在Eclipse中,我也可以找到特定方法的用法,但這不是我想要實現的。我會更新這個問題。 – Tarek

2

我希望我能正確理解你的問題。我想你可以使用grep -Irns函數來查找呼叫。您可以grepgetA().getA2()。這將返回函數被調用的行和行號。

2

而不是掃描用於向方法getCurrentA所有引用做用於向A所有引用的掃描。

這會告訴你在你的程序中使用了哪個類,你可能會發現手工掃描該列表更容易,並決定是否需要針對每個找到的結果採取行動,而不是試圖做任何事情。

4

根據你使用這個問題的IDE更容易找到。

Eclipse IDE已經存在,你只需要把要找到並執行Ctrl + Alt + H 這會給你的整個層次結構,其方法是用在方法聲明鼠標最有潛力的調用層次的模塊之一你想分析的方法。

此外,Call Hierarchy模塊提供了一種模式,您可以在其中找到您的方法正在調用的方法。

一些額外的信息:http://help.eclipse.org/indigo/index.jsp?topic=%2Forg.eclipse.cdt.doc.user%2Freference%2Fcdt_u_call_hierarchy_view.htm

1

最簡單的方式找到通話費用是用在eclipse引用,但有一個有趣的方式 :

  1. 更改方法名B.getCurrentAA()
  2. 生成項目
  3. 項目與錯誤編譯
  4. 轉到標記部分,看用法錯誤,並找到你的使用meth的od
0

我認爲IntelliJ可以解決您的問題。它有一個「分析數據流」功能,我認爲這是在幫你在找什麼:

這裏是我的示例代碼:

public class Main { 

    private static A a = new A(); //nevermind the way it is initialized 

    public static A getA(){ 
     return a; 
    } 

    public void method(){ 
     A myA = getA(); 
     Integer a1 = myA.getA1(); //this line is found 
     Integer a2 = myA.getA2(); //this line is found 
    } 

    public void anotherMethod(){ 
     A myA = new A();   
     Integer a1 = myA.getA1(); //this line is NOT found 
     Integer a2 = myA.getA2(); //this line is NOT found 
    } 
} 

運行「從這裏分析數據流」(用光標在return a;線)給我這個:

enter image description here

對不起你只提供一個解決方案具有的IntelliJ(用的IntelliJ-13終極版測試)