这个解释起来有点棘手。我有一个 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...
这是我必须手动执行的示例:
- 使用 Eclipse 的搜索查找对 B.getCurrentA() 的所有调用;
打开找到的第一个方法,假设是下面的方法:
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); } }
打开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 } }
打开CBusinessObject类中的verifyA方法,确定使用了字段a2:
public class CBusinessObject { public void verifyA(c) { if (Integer.valueOf(1).equals(c.getA().getA2())) { // do stuff else { // something else } } }
对接下来的 216 场比赛重复第 2-4 步...好。
请帮忙。
最佳答案
如果您想进行任何源代码更改/重构,您将必须手动查找所有用法并应用您的代码更改;
无论如何,我有两种不同的方法
静态搜索 您可以简单地在 Eclipse 中执行
Text Search
来查找getA2()
的出现。它会直接带你到 Caller 方法(这里是 CBusinessObject.verifyA())——但它会给你每一次 getA2() 的出现,可能来自不同的类运行时搜索 使用
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/