繁体   English   中英

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

[英]How can I get the complete Call Hierarchy of a Java source code?

解释这有点棘手。 我有一个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. 打开找到的第一个方法,让我们说它是下面的方法:

     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 ......耶。

请帮忙。

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

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

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

  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文件(你必须设置带有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

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

Eclipse IDE具有最现有的Call Hierarchy模块之一,您只需将鼠标放在要查找的方法声明中并执行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

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

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

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

而不是扫描用于向所述方法的所有引用getCurrentA做用于向的所有引用的扫描A

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

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

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

我认为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;行)给我这个:

在此输入图像描述

很抱歉只为您提供IntelliJ解决方案(使用IntelliJ-13 Ultimate Edition测试)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM