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

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

我需要找到类 A 由 B 返回 的所有用法。假设 C 类调用了 c.setA(B.getCurrentA()) 然后进一步调用了 c.getA().getA2();,我我想找到所有这些。

在实际场景中,我有 217 个调用 B.getCurrentA() 的不同类。我无法手动跟踪 Eclipse 中的所有调用并找出调用了哪些方法。

Eclipse 调用层次结构 View 只显示对 B.getCurrentA() 的所有调用。

我怎样才能做到这一点?


编辑

Chris Hayes 明白我想做什么。为了在不破坏整个系统的情况下重构一些非常糟糕的遗留代码,我需要首先使用 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类中的search方法:

    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 步 - 编写 Agent 主类以启动检测

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 类设置 list 文件,并添加 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

关于java - 如何获得 Java 源代码的完整调用层次结构?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20589962/

相关文章:

java - 如何列出在特定类加载器中加载的所有类

java - Junit 与 HSQL/H2 一起使用,无需 Spring/Hibernate

java - Logback 类 CircularityError

java - 共享 Eclipse 配置首选项

Eclipse subsersive - 删除文件但将其保留在本地

android - Intellij Idea 13 在编写 Android 应用程序时找不到 Hierarchy Tools

java - 如何运行 PING 命令并获取 ping 主机摘要?

java - JVM 是否进行运行时常量折叠?

java - 如何为在anaconda环境中运行的java程序设置RAM和CPU参数?

ios - 在 View 不在窗口层次结构中的 PFLogInViewController 上显示 *.viewController