gpt4 book ai didi

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

转载 作者:搜寻专家 更新时间:2023-10-30 19:54:04 26 4
gpt4 key购买 nike

这个解释起来有点棘手。我有一个 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/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com