如何在 Java 中找到调用给定方法的所有方法?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/930289/
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 find all the methods that call a given method in Java?
提问by arthur
I need to get a list of all caller methods for a method of interest for me in Java. Is there a tool that can help me with this?
我需要获取我在 Java 中感兴趣的方法的所有调用方方法的列表。有没有工具可以帮助我解决这个问题?
Edit: I forgot to mention that I need to do this from a program. I'm usig Java Pathfinder and I want to run it an all the methods that call my method of interest.
编辑:我忘了提到我需要从程序中执行此操作。我正在使用 Java Pathfinder,我想运行它以及调用我感兴趣的方法的所有方法。
采纳答案by Chadwick
For analyzing bytecode, I would recommend ASM. Given a list of Classes to analyze, a visitor can be made which finds the method calls you're interested in. One implementation which analyses classes in a jar file is below.
为了分析字节码,我会推荐ASM。给定要分析的类列表,可以创建一个访问者来查找您感兴趣的方法调用。下面是一个分析 jar 文件中的类的实现。
Note that ASM uses internalNameswith '/' instead of '.' as a separator. Specify the target method as a standard declarationwithout modifiers.
请注意,ASM 使用带有“/”而不是“.”的内部名称。作为分隔符。将目标方法指定为不带修饰符的标准声明。
For example, to list methods that could be calling System.out.println("foo") in the java runtime jar:
例如,要列出可以在 java 运行时 jar 中调用 System.out.println("foo") 的方法:
java -cp "classes;asm-3.1.jar;asm-commons-3.1.jar" App \
c:/java/jdk/jre/lib/rt.jar \
java/io/PrintStream "void println(String)"
Edit: source and line numbers added: Note that this only indicates the last target method invocation per calling method - the original q only wanted to know whichmethods. I leave it as an exercise for the reader to show line numbers of the calling method declaration, or the line numbers of every target invocation, depending on what you're actually after. :)
编辑:添加的源代码和行号:请注意,这仅表示每个调用方法的最后一个目标方法调用 - 原始 q 只想知道哪些方法。我把它作为一个练习让读者展示调用方法声明的行号,或者每个目标调用的行号,这取决于你真正想要的是什么。:)
results in:
结果是:
LogSupport.java:44 com/sun/activation/registries/LogSupport log (Ljava/lang/String;)V
LogSupport.java:50 com/sun/activation/registries/LogSupport log (Ljava/lang/String;Ljava/lang/Throwable;)V
...
Throwable.java:498 java/lang/Throwable printStackTraceAsCause (Ljava/io/PrintStream;[Ljava/lang/StackTraceElement;)V
--
885 methods invoke java/io/PrintStream println (Ljava/lang/String;)V
source:
来源:
public class App {
private String targetClass;
private Method targetMethod;
private AppClassVisitor cv;
private ArrayList<Callee> callees = new ArrayList<Callee>();
private static class Callee {
String className;
String methodName;
String methodDesc;
String source;
int line;
public Callee(String cName, String mName, String mDesc, String src, int ln) {
className = cName; methodName = mName; methodDesc = mDesc; source = src; line = ln;
}
}
private class AppMethodVisitor extends MethodAdapter {
boolean callsTarget;
int line;
public AppMethodVisitor() { super(new EmptyVisitor()); }
public void visitMethodInsn(int opcode, String owner, String name, String desc) {
if (owner.equals(targetClass)
&& name.equals(targetMethod.getName())
&& desc.equals(targetMethod.getDescriptor())) {
callsTarget = true;
}
}
public void visitCode() {
callsTarget = false;
}
public void visitLineNumber(int line, Label start) {
this.line = line;
}
public void visitEnd() {
if (callsTarget)
callees.add(new Callee(cv.className, cv.methodName, cv.methodDesc,
cv.source, line));
}
}
private class AppClassVisitor extends ClassAdapter {
private AppMethodVisitor mv = new AppMethodVisitor();
public String source;
public String className;
public String methodName;
public String methodDesc;
public AppClassVisitor() { super(new EmptyVisitor()); }
public void visit(int version, int access, String name,
String signature, String superName, String[] interfaces) {
className = name;
}
public void visitSource(String source, String debug) {
this.source = source;
}
public MethodVisitor visitMethod(int access, String name,
String desc, String signature,
String[] exceptions) {
methodName = name;
methodDesc = desc;
return mv;
}
}
public void findCallingMethodsInJar(String jarPath, String targetClass,
String targetMethodDeclaration) throws Exception {
this.targetClass = targetClass;
this.targetMethod = Method.getMethod(targetMethodDeclaration);
this.cv = new AppClassVisitor();
JarFile jarFile = new JarFile(jarPath);
Enumeration<JarEntry> entries = jarFile.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
if (entry.getName().endsWith(".class")) {
InputStream stream = new BufferedInputStream(jarFile.getInputStream(entry), 1024);
ClassReader reader = new ClassReader(stream);
reader.accept(cv, 0);
stream.close();
}
}
}
public static void main( String[] args ) {
try {
App app = new App();
app.findCallingMethodsInJar(args[0], args[1], args[2]);
for (Callee c : app.callees) {
System.out.println(c.source+":"+c.line+" "+c.className+" "+c.methodName+" "+c.methodDesc);
}
System.out.println("--\n"+app.callees.size()+" methods invoke "+
app.targetClass+" "+
app.targetMethod.getName()+" "+app.targetMethod.getDescriptor());
} catch(Exception x) {
x.printStackTrace();
}
}
}
回答by Simon Groenewolt
Edit: the original question was edited to indicate a runtime solution was needed - this answer was given before that edit and only indicates how to do it during development.
编辑:原始问题已被编辑以表明需要运行时解决方案 - 此答案是在该编辑之前给出的,并且仅指示在开发期间如何进行。
If you are using Eclipse you can right click the method and choose "Open call hierarchy" to get this information.
如果您使用 Eclipse,您可以右键单击该方法并选择“打开调用层次结构”以获取此信息。
Updated after reading comments: Other IDEs support this as well in a similar fashion (at least Netbeans and IntelliJ do)
阅读评论后更新:其他 IDE 也以类似的方式支持这一点(至少 Netbeans 和 IntelliJ 支持)
回答by Christoffer
Yes, most modern IDE:s will let you either search for usages of a method or variable. Alternatively, you could use a debugger and set a trace point on the method entry, printing a stack trace or whatever every time the method is invoked. Finally, you could use some simple shell util to just grep for the method, such as
是的,大多数现代 IDE:s 都可以让您搜索方法或变量的用法。或者,您可以使用调试器并在方法条目上设置跟踪点,在每次调用方法时打印堆栈跟踪或其他任何内容。最后,您可以使用一些简单的 shell util 来为该方法提供 grep,例如
find . -name '*.java' -exec grep -H methodName {} ;
The only method that will let you find invokations made through some reflection method, though, would be using the debugger.
但是,让您找到通过某种反射方法进行的调用的唯一方法是使用调试器。
回答by soldier.moth
The closest that I could find was the method described in this StackOverflow questions selected answer.check this out
我能找到的最接近的是这个 StackOverflow 问题选择答案中描述的方法。看一下这个
回答by GreenieMeanie
You can do this with something in your IDE such as "Find Usages" (which is what it is called in Netbeans and JDeveloper). A couple of things to note:
您可以使用 IDE 中的某些内容来执行此操作,例如“查找用法”(在 Netbeans 和 JDeveloper 中就是这样称呼的)。有几点需要注意:
- If your method implements a method from an interface or base class, you can only know that your method is POSSIBLY called.
- A lot of Java frameworks use Reflection to call your method (IE Spring, Hibernate, JSF, etc), so be careful of that.
- On the same note, your method could be called by some framework, reflectively or not, so again be careful.
- 如果您的方法实现了来自接口或基类的方法,您只能知道您的方法可能被调用。
- 许多 Java 框架使用反射来调用您的方法(IE Spring、Hibernate、JSF 等),所以要小心。
- 同样,您的方法可能会被某个框架调用,无论是否反射,所以再次小心。
回答by Jared
There isn't a way to do this (programmatically) via the Java reflection libraries - you can't ask a java.lang.reflect.Method"which methods do you call?"
没有办法通过 Java 反射库(以编程方式)执行此操作 - 您不能询问java.lang.reflect.Method“您调用哪些方法?”
That leaves two other options I can think of:
这剩下我能想到的另外两个选项:
Static analysis of the source code. I'm sure this is what the Eclipse Java toolset does - you could look at the Eclipse source behind the JDT, and find what it does when you ask Eclipse to "Find References" to a method.
Bytecode analysis. You could inspect the bytecode for calls to the method. I'm not sure what libraries or examples are out there to help with this - but I can't imagine that something doesn't exist.
源代码的静态分析。我确信这就是 Eclipse Java 工具集所做的 - 您可以查看 JDT 背后的 Eclipse 源代码,并在您要求 Eclipse “查找引用”方法时找到它的作用。
字节码分析。您可以检查字节码以获取对该方法的调用。我不确定有哪些库或示例可以帮助解决这个问题 - 但我无法想象有些东西不存在。
回答by Robert Munteanu
Annotate the method with @Deprecated ( or tag it with @deprecated ), turn on deprecation warnings, run your compile and see which warnings get triggered.
使用 @Deprecated 注释方法(或使用 @deprecated 标记它),打开弃用警告,运行编译并查看触发了哪些警告。
The run your compile bit can be done either by invoking an external ant process orby using the Java 6 compiler API.
可以通过调用外部 ant 进程或使用Java 6 编译器 API来运行您的编译位。
回答by Niyaz
In eclipse, highlight the method name and then Ctrl+Shift+G
在 Eclipse 中,突出显示方法名称,然后按 Ctrl+Shift+G
回答by Bharath V
- right click on method
- Go to references and (depending on your requirement)
choose workspace/project/Hierarchy.
- 右键单击方法
- 转到参考并(根据您的要求)
选择工作区/项目/层次结构。
This pops up a panel that shows all references to this functions. Eclipse FTW !
这会弹出一个面板,显示对这个函数的所有引用。日食 FTW!
回答by rpvilao
I made a small example using @Chadwick's one. It's a test that assesses if calls to getDatabaseEngine() are made by methods that implement @Transaction.
我用@Chadwick 的例子做了一个小例子。这是一个测试,用于评估对 getDatabaseEngine() 的调用是否由实现 @Transaction 的方法进行。
/**
* Ensures that methods that call {@link DatabaseProvider#getDatabaseEngine()}
* implement the {@link @Transaction} annotation.
*
* @throws Exception If something occurs while testing.
*/
@Test
public void ensure() throws Exception {
final Method method = Method.getMethod(
DatabaseEngine.class.getCanonicalName() + " getDatabaseEngine()");
final ArrayList<java.lang.reflect.Method> faultyMethods = Lists.newArrayList();
for (Path p : getAllClasses()) {
try (InputStream stream = new BufferedInputStream(Files.newInputStream(p))) {
ClassReader reader = new ClassReader(stream);
reader.accept(new ClassAdapter(new EmptyVisitor()) {
@Override
public MethodVisitor visitMethod(final int access, final String name, final String desc, final String signature, final String[] exceptions) {
return new MethodAdapter(new EmptyVisitor()) {
@Override
public void visitMethodInsn(int opcode, String owner, String nameCode, String descCode) {
try {
final Class<?> klass = Class.forName(Type.getObjectType(owner).getClassName());
if (DatabaseProvider.class.isAssignableFrom(klass) &&
nameCode.equals(method.getName()) &&
descCode.equals(method.getDescriptor())) {
final java.lang.reflect.Method method = klass.getDeclaredMethod(name,
getParameters(desc).toArray(new Class[]{}));
for (Annotation annotation : method.getDeclaredAnnotations()) {
if (annotation.annotationType().equals(Transaction.class)) {
return;
}
}
faultyMethods.add(method);
}
} catch (Exception e) {
Throwables.propagate(e);
}
}
};
}
}, 0);
}
}
if (!faultyMethods.isEmpty()) {
fail("\n\nThe following methods must implement @Transaction because they're calling getDatabaseEngine().\n\n" + Joiner.on("\n").join
(faultyMethods) + "\n\n");
}
}
/**
* Gets all the classes from target.
*
* @return The list of classes.
* @throws IOException If something occurs while collecting those classes.
*/
private List<Path> getAllClasses() throws IOException {
final ImmutableList.Builder<Path> builder = new ImmutableList.Builder<>();
Files.walkFileTree(Paths.get("target", "classes"), new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException {
if (file.getFileName().toString().endsWith(".class")) {
builder.add(file);
}
return FileVisitResult.CONTINUE;
}
});
return builder.build();
}
/**
* Gets the list of parameters given the description.
*
* @param desc The method description.
* @return The list of parameters.
* @throws Exception If something occurs getting the parameters.
*/
private List<Class<?>> getParameters(String desc) throws Exception {
ImmutableList.Builder<Class<?>> obj = new ImmutableList.Builder<>();
for (Type type : Type.getArgumentTypes(desc)) {
obj.add(ClassUtils.getClass(type.getClassName()));
}
return obj.build();
}