如何获得Java源代码的完整调用层次结构?

时间:2013-12-13 17:38:02

标签: java eclipse hierarchy

这有点难以解释。我有一个A类:

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

有一个静态类B返回我的类A:

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的投影微调一些查询(系统中的每个映射实体都被急切加载,并且许多实体是相关的,因此一些查询需要很长时间时间取出一切)。但首先我需要找到使用哪些属性,以便我不会在某处获得NullPointerException ...

以下是我必须手动完成的示例:

  1. 使用Eclipse的搜索查找对B.getCurrentA();
  2. 的所有调用
  3. 打开找到的第一个方法,让我们说它是下面的方法:

    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);
        }
    }
    
  4. 在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
        }
    }
    
  5. 在CBusinessObject类中打开verifyA方法并识别使用的字段a2:

    public class CBusinessObject {
        public void verifyA(c) {
            if (Integer.valueOf(1).equals(c.getA().getA2())) {
                // do stuff
            else {
                // something else
            }
        }
    }
    
  6. 对接下来的216场比赛重复步骤2-4 ......耶。

  7. 请帮忙。

7 个答案:

答案 0 :(得分:12)

如果您想要更改/重构任何源代码,您必须手动查找所有用法并应用代码更改;

无论如何,我有两种不同的方式

  1. 静态搜索     你可以在eclipse中简单地做Text Search来找到getA2()的出现。它会直接带你到Caller方法(这里是CBusinessObject.verifyA()) - 但是它会给你每个getA2()的出现,可能来自不同的类

  2. 运行时搜索     使用java instrumentation API在运行时更改所需方法的字节代码以查找调用类并运行为java agent - 使您能够在不触及现有代码库的情况下识别调用者并且非常有用,尤其是在您无权访问源代码。

  3. 在这里,您将了解如何实施

    步骤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文件(您必须设置包含premain类的清单文件,并添加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

答案 1 :(得分:4)

根据您使用的IDE,此问题更容易找到。

Eclipse IDE存在最可能的Call Hierarchy模块之一,您只需将鼠标放在要查找的方法声明中并执行Ctrl + Alt + H 这将为您提供使用您要分析的方法的方法的整个层次结构。

Call Alierarchy模块还提供了一种模式,您可以在其中找到方法调用的方法。

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

答案 2 :(得分:2)

在IntelliJ IDEA中,如果要查找c.getA().getA2();的用法,请右键单击A.a2并选择“查找用法”。同样适用于A.a1B.getCurrentA()。未使用的字段和方法在IDEA中以不同的颜色显示。我听说IntelliJ比Eclipse有更多的重构能力,但我敢打赌Eclipse做同样的事情,只是略有不同。

此外,使用grep,find和sed,您可以搜索相应的方法,只搜索与A相同的文件包或导入A的文件,或者按名称拼写出来。

答案 3 :(得分:2)

我希望我能正确理解你的问题。我认为您可以使用grep -Irns函数来查找调用。您grep可以getA().getA2()。这将返回调用函数的行以及行号。

答案 4 :(得分:2)

不扫描所有对方法的引用 getCurrentA,而是扫描所有对 Class A的引用。

这将向您显示程序中使用该类的所有位置,您可能会发现更容易通过手动扫描该列表并决定是否需要对找到的每个结果采取行动而不是尝试做任何花哨的事情。

答案 5 :(得分:1)

查找Call Usage的最简单方法是在eclipse中使用引用,但有一种有趣的方式 :

  1. 将方法名称更改为B.getCurrentAA()
  2. 构建项目
  3. 您的项目编译错误
  4. 转到标记部分,查看使用错误并查找方法的用法

答案 6 :(得分: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 Ultimate Edition测试)