2013-12-13 17 views
13

To jest trochę trudne do wytłumaczenia. Mam Klasa A:Jak uzyskać pełną hierarchię połączeń kodu źródłowego Java?

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

Jest statyczna klasa B, która zwraca moją Klasa A:

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

muszę znaleźć wszystkie zwyczaje klasy A zwracane przez B. Powiedzmy, że klasa C zadzwoni na numer c.setA(B.getCurrentA()), a następnie będzie wywoływać numer c.getA().getA2();. Chciałbym je wszystkie znaleźć.

W prawdziwym scenariuszu mam 217 różnych klas, które wywołują B.getCurrentA(). Nie mogę ręcznie śledzić wszystkich wywołań w Eclipse i dowiedzieć się, które metody są wywoływane.

Widok hierarchii połączeń Eclipse wyświetla tylko wszystkie połączenia z B.getCurrentA().

Jak mogę to osiągnąć?


EDIT

Chris Hayes rozumieć to, co chcę robić. W celu refaktoryzacji naprawdę złego kodu starszego bez przerywania całego systemu, muszę najpierw dokładnie dostroić niektóre zapytania za pomocą projekcji Hibernate'a (każda zmapowana jednostka w systemie jest niecierpliwie załadowana, a wiele encji jest powiązanych, więc niektóre zapytania trwają długo czas przynosi wszystko). Ale najpierw muszę dowiedzieć się, jakie właściwości są używane tak, aby nie uzyskać NullPointerException gdzieś ...

Oto przykład tego, co musiałbym zrobić ręcznie: Szukaj

  1. Skorzystaj z Eclipse do znajdź wszystkie wywołania do B.getCurrentA();
  2. otwarta pierwsza metoda znaleźć, powiedzmy, że jest to jeden poniżej:

    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. Otwarta metoda wyszukiwania w klasie 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. Otwarta metoda verifyA w klasie CBusinessObject i określić, że pole a2 jest używane:

    public class CBusinessObject { 
        public void verifyA(c) { 
         if (Integer.valueOf(1).equals(c.getA().getA2())) { 
          // do stuff 
         else { 
          // something else 
         } 
        } 
    } 
    
  5. Powtórz kroki 2-4 dla kolejnych 216 dopasowań ... Yay.

Proszę o pomoc.

+0

Jeśli interesuje Cię bardzo specyficzne wystąpienie 'B.getCurrentA()' i masz dostęp do kodu, w którym jest on wywoływany, możesz być w stanie wydrukować bieżący ślad stosu do dziennika. Lub ustaw tam punkt przerwania, a następnie uruchom program, aby wykonywanie zostało zatrzymane w punkcie przerwania. – FrustratedWithFormsDesigner

+1

Wypróbuj 'Thread.dumpStack()', aby zobaczyć, z której metody została wywołana – kajacx

+1

To znacznie więcej niż hierarchia połączeń. Chcesz śledzić * wszędzie, gdzie zwrócona wartość jest przechowywana i używana *. Brzmi to jak monumentalne zadanie analizy statycznej, aby spróbować określić, do której wartości może dotrzeć. –

Odpowiedz

12

Jeśli chcesz dokonać zmian/refaktoryzacji kodu źródłowego, będziesz musiał ręcznie znaleźć wszystkie zastosowania i zastosować zmiany kodu;

Każdy sposób, mam dwa odmienne cierpliwe

  1. Static przeszukiwanie Można po prostu zrobić Text Search w Eclipse znaleźć występowaniu gazów getA2(). Będzie ona bezpośrednio przeniesie Cię do metody rozmówcy (tutaj CBusinessObject.verifyA()) -ale to daje każdy getA2() wystąpień, mogą być z innej klasy

  2. wyszukiwania czas Run użytkowania java instrumentation API zmienić kod bajtowy w czasie wykonywania na wymaganej przez Ciebie metodzie znajdowania klasy wywołującej i uruchamiania jako java agent - Umożliwia identyfikowanie dzwoniącego bez dotykania istniejącej bazy kodu i bardzo przydatne, szczególnie gdy nie masz dostępu do kodu źródłowego.

Tu idziesz jak wdrożyć

Krok 1- Zapis pełnomocnik główny klasa zainicjować oprzyrządowanie

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

wdrażania Krok 2 -Write ClassFileTransformer i uchwycić metodę

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; 
      } 

Krok 3- utworzyć plik jar dla klas agentów (trzeba ustawić plik manifestu z klasą premain i dodać javaassit słoik) fragment pliku kompilacji jest podana - można zrobić to ręcznie, jak również

<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> 

Krok 4 Uruchom aplikację z głównego agenta java - przed tym zestawie VM argumentów do agenta

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

Pre załadować requisite: potrzebowałbyś javassist.jar w ścieżce klasy.

+0

Hej, przepraszam za opóźnienie! Skończyło się na użyciu hierarchii wywołań (Ctrl + Alt + H) na pobierających. – Tarek

2

W IntelliJ IDEA, jeśli chcesz znaleźć zastosowanie c.getA().getA2(); kliknij prawym przyciskiem myszy na A.a2 i wybierz "znajdź zwyczaje". Podobnie dla A.a1 i B.getCurrentA(). Niewykorzystane pola i metody pojawiają się w innym kolorze w IDEA. Słyszałem, że IntelliJ ma więcej mocy refaktoryzacji niż Eclipse, ale założę się, że Eclipse robi to samo, tylko trochę inaczej.

Ponadto, używając grep, find i sed, możesz wyszukać odpowiednie metody, tylko w plikach, które znajdują się w tym samym pakiecie co A lub import A lub przeliterować je według nazwy.

+0

W Eclipse mogę również znaleźć zastosowanie określonej metody, ale nie jest to tym, co chcę osiągnąć. Zaktualizuję pytanie. – Tarek

2

Mam nadzieję, że dobrze zrozumiałem twoje pytanie. Myślę, że możesz użyć funkcji grep -Irns, aby znaleźć połączenia. Możesz grep dla getA().getA2(). To spowoduje powrót do linii, z których wywoływane są funkcje wraz z numerami linii.

2

Zamiast skanowania wszystkich odniesień do sposobugetCurrentAdo skanowania dla wszystkich odniesień do klasyA.

To pokaże ci wszędzie, gdzie klasa jest używana w twoim programie i prawdopodobnie znajdziesz ją łatwiejszą do przeskanowania i zeskanowania tej listy ręcznie i zdecydujesz, czy musisz działać na każdym znalezionym wyniku, niż próbować zrobić coś wymyślnego. .

4

W zależności od IDE, z którego korzystasz, ten problem jest łatwiejszy do znalezienia.

Eclipse IDE posiada jedną z najbardziej potencjalnych modułów połączeń Hierarchy istniejących, wystarczy umieścić kursor myszy w deklaracji metody, którą chcesz znaleźć i wykonać Ctrl + Alt + H To daje całą hierarchię, która metoda jest za pomocą metoda, którą chcesz przeanalizować.

Również moduł Hierarchia połączeń oferuje tryb, w którym można znaleźć metody wywoływane przez metodę.

Niektóre dodatkowa informacja: http://help.eclipse.org/indigo/index.jsp?topic=%2Forg.eclipse.cdt.doc.user%2Freference%2Fcdt_u_call_hierarchy_view.htm

1

Najłatwiej znaleźć Zadzwoń Wykorzystanie używa referencji w Eclipse, ale jest zabawny sposób :

  1. Zmiana nazwy metody B.getCurrentAA()
  2. Budowanie projektu
  3. swój projekt kompiluje się z powodu błędu
  4. idź do towarowe część i zobaczyć użytkowaniu błąd i Znajdź wykorzystania twoich met od
0

Myślę, że IntelliJ może rozwiązać twój problem. To masz „Analiza przepływu danych” funkcji i myślę, że robi to, czego szukasz:

Oto mój przykładowy kod:

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 
    } 
} 

Uruchamianie „Analiza przepływu danych stąd” (z kursorem na return a; linia) dają mi ten:

enter image description here

przykro zapewni Ci tylko rozwiązanie z IntelliJ (testowane z IntelliJ-13 Ultimate Edition)

Powiązane problemy