如何获得 Java 源代码的完整调用层次结构?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/20589962/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How can I get the complete Call Hierarchy of a Java source code?
提问by Tarek
This is a bit tricky to explain. I have a class A:
这有点难以解释。我有一个A类:
public class A {
private Integer a1;
private Integer a2;
// getters and setters.
}
There is a static class B that returns my class A:
有一个静态类 B 返回我的类 A:
public static class B {
public static A getCurrentA() {
return a;
}
}
I need to find all usages of class A returned by B. So let's say class C calls c.setA(B.getCurrentA())
and then further along there's a call to c.getA().getA2();
, I'd want to find all of these.
我需要找到B 返回的 A类的所有用法。因此,假设 C 类调用c.setA(B.getCurrentA())
,然后进一步调用c.getA().getA2();
,我想找到所有这些。
In the real scenario, I have 217 different classes that call B.getCurrentA()
. I can't manually follow all the calls in Eclipse and find out which methods are getting called.
在实际场景中,我有 217 个不同的类调用B.getCurrentA()
. 我无法手动跟踪 Eclipse 中的所有调用并找出调用了哪些方法。
Eclipse call hierarchy view only shows me all calls to B.getCurrentA()
.
Eclipse 调用层次结构视图只显示所有对B.getCurrentA()
.
How can I achieve this?
我怎样才能做到这一点?
EDIT
编辑
Chris Hayes understood what I want to do. In order to refactor some really bad legacy code without breaking the whole system, I need to first fine-tune some queries using Hibernate's projections (every mapped entity in the system is eagerly loaded, and many entities are related, so some queries take a LONG time fetching everything). But first I need to find which properties are used so that I don't get a NullPointerException somewhere...
克里斯海耶斯明白我想做什么。为了在不破坏整个系统的情况下重构一些非常糟糕的遗留代码,我需要首先使用 Hibernate 的投影微调一些查询(系统中的每个映射实体都是急切加载的,并且许多实体是相关的,所以有些查询需要很长时间)时间获取一切)。但首先我需要找到使用了哪些属性,以便我不会在某处收到 NullPointerException ......
Here's an example of what I'd have to do manually:
这是我必须手动执行的操作的示例:
- Use Eclipse's Search to find all calls to B.getCurrentA();
Open the first method found, let's say it's the one below:
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); } }
Open the search method in the CFacade class:
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 } }
Open the verifyA method in the CBusinessObject class and identify that field a2 is used:
public class CBusinessObject { public void verifyA(c) { if (Integer.valueOf(1).equals(c.getA().getA2())) { // do stuff else { // something else } } }
Repeat steps 2-4 for the next 216 matches... Yay.
- 使用 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 类中打开搜索方法:
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... 是的。
Please help.
请帮忙。
采纳答案by Satheesh Cheveri
If you want to make any source code changes/refactoring you will have to manually find all usages and apply your code changes;
如果您想进行任何源代码更改/重构,您必须手动查找所有用法并应用您的代码更改;
Any way, I have two different aproach
无论如何,我有两种不同的方法
Static search You can simply do
Text Search
in eclipse to find the occurance ofgetA2()
. It will directly take you to the Caller method (here CBusinessObject.verifyA()) -but it will give you every getA2() occurances, may be from different classRun time search Use
java instrumentation API
to change the byte code at run time on your required method to find invoking class and run asjava agent
- Enable you to identify the caller with out touching the existing code base and very useful especially when you don't have access to source code.
静态搜索您可以简单地
Text Search
在 eclipse 中查找getA2()
. 它会直接带你到 Caller 方法(这里是 CBusinessObject.verifyA()) - 但它会给你每个 getA2() 出现,可能来自不同的类运行时搜索 用于
java instrumentation API
在运行时更改所需方法的字节码,以查找调用类并运行为java agent
- 使您无需接触现有代码库即可识别调用者,非常有用,尤其是当您无法访问源代码时代码。
Here you go how to implement
教你如何实现
Step 1- Write Agent main class to initiate instrumentation
第 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);
}
}
Step 2 -Write a ClassFileTransformer implementation and capture the method
第 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;
}
Step 3- create jar file for agent classes ( you have to set manifest file with premain class, and add javaassit jar)snippet of build file is given - you can do it by manually as well
第 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>
Step 4- Run your main application with java agent - before that set VM arguments to load agent
第 4 步 - 使用 Java 代理运行主应用程序 - 在此之前设置 VM 参数以加载代理
-`javaagent:D:\softwares\AgentProject\AgentLib\build\jar\BasicAgent.jar`
Pre requisite : you would need javassist.jar
in the class path.
先决条件:您需要javassist.jar
在类路径中。
回答by GlenPeterson
In IntelliJ IDEA, if you want to find usages of c.getA().getA2();
right-click on A.a2
and choose "find usages." Similarly for A.a1
and B.getCurrentA()
. Unused fields and methods show up in a different color in IDEA. I've heard that IntelliJ has more refactoring power than Eclipse, but I bet Eclipse does the same thing, just slightly differently.
在 IntelliJ IDEA 中,如果要查找用法,请c.getA().getA2();
右键单击A.a2
并选择“查找用法”。A.a1
和类似B.getCurrentA()
。未使用的字段和方法在 IDEA 中以不同的颜色显示。我听说 IntelliJ 比 Eclipse 具有更强的重构能力,但我敢打赌 Eclipse 会做同样的事情,只是略有不同。
Also, using grep, find, and sed, you can search for the appropriate methods, just in files that are in the same package as A or that import A, or spell it out by name.
此外,使用 grep、find 和 sed,您可以搜索适当的方法,只需在与 A 位于同一包或导入 A 的文件中,或按名称拼写即可。
回答by SaurabhJinturkar
I hope I understood your question correctly. I think you can use grep -Irns
function to find the calls. You can grep
for getA().getA2()
. That will return lines from where functions are called along with line numbers.
我希望我正确理解了你的问题。我认为您可以使用grep -Irns
函数来查找调用。你可以grep
为getA().getA2()
. 这将返回调用函数的行以及行号。
回答by Tim B
Rather than scanning for all references to the methodgetCurrentA
do a scan for all references to the ClassA
.
不是扫描对方法的所有引用,而是扫描对Class 的getCurrentA
所有引用。A
This will show you everywhere that class is used within your program and you will probably find it is easier to go through and scan that list by hand and decide if you need to act on each result found than trying to do anything fancy.
这将向您显示在您的程序中使用该类的任何地方,您可能会发现手动浏览和扫描该列表并决定是否需要对找到的每个结果采取行动比尝试做任何花哨的事情更容易。
回答by Kuraokami
Depending on the IDE you are using this problem is simpler to find.
根据您使用的 IDE,这个问题更容易找到。
Eclipse IDE has one of the most potential Call Hierarchy modules existing, you just need to put the mouse in the method declaration that you want to find and execute Ctrl + Alt + H
This will give you the entire hierarchy of which method is using the method you want to analyze.
Eclipse IDE 具有现有的最有潜力的 Call Hierarchy 模块之一,您只需要将鼠标放在要查找和执行的方法声明中,Ctrl + Alt + H
这将为您提供哪个方法正在使用您要分析的方法的整个层次结构。
Also the Call Hierarchy module offers a mode where you can find the methods that your method is calling.
Call Hierarchy 模块还提供了一种模式,您可以在其中找到您的方法正在调用的方法。
Some extra info: http://help.eclipse.org/indigo/index.jsp?topic=%2Forg.eclipse.cdt.doc.user%2Freference%2Fcdt_u_call_hierarchy_view.htm
一些额外信息:http: //help.eclipse.org/indigo/index.jsp?topic=%2Forg.eclipse.cdt.doc.user%2Freference%2Fcdt_u_call_hierarchy_view.htm
回答by Amin Bahiraei
The easiest way to find Call Usage is using references in eclipse,but there is a funny way :
找到 Call Usage 的最简单方法是在 eclipse 中使用引用,但有一个有趣的方法:
- Change method name to B.getCurrentAA()
- Build your Project
- Your Project compiles with error
- Go to Marks Part and see usage Error And Find Usage Of your method
- 将方法名称更改为 B.getCurrentAA()
- 构建你的项目
- 您的项目编译出错
- 转到标记部分并查看用法错误并查找方法的用法
回答by ben75
I think IntelliJ can solve your problem. It have an "Analyze dataflow" feature and I think it is doing what you are looking for:
我认为 IntelliJ 可以解决您的问题。它有一个“分析数据流”功能,我认为它正在做你正在寻找的东西:
Here is my sample code:
这是我的示例代码:
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
}
}
Running the "Analyze dataflow from here" (with cursor on return a;
line) give me this:
运行“从这里分析数据流”(光标return a;
在线)给我这个:
Sorry to provide you only a solution with IntelliJ (tested with IntelliJ-13 Ultimate Edition)
很抱歉只为您提供 IntelliJ 的解决方案(使用 IntelliJ-13 Ultimate Edition 测试)