如何在Java中找到调用给定方法的所有方法?

art*_*hur 32 java

我需要获取Java中感兴趣的方法的所有调用方法的列表.有没有可以帮助我的工具?

编辑:我忘了提到我需要从程序中执行此操作.我使用Java Pathfinder,我想运行它调用我感兴趣的方法的所有方法.

Cha*_*ick 44

为了分析字节码,我建议使用ASM.给定要分析的类列表,可以创建一个查找您感兴趣的方法调用的访问者.下面是一个分析jar文件中的类的实现.

请注意,ASM使用带有'/'的internalNames而不是'.' 作为分隔符.将目标方法指定为不带修饰符的标准声明.

例如,要列出可以在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)"
Run Code Online (Sandbox Code Playgroud)

编辑:添加源和行号:请注意,这仅表示每个调用方法的最后一个目标方法调用 - 原始q只想知道哪些方法.我把它作为练习让读者显示调用方法声明的行号,或每个目标调用的行号,具体取决于你实际使用的内容.:)

结果是:

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
Run Code Online (Sandbox Code Playgroud)

资源:

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();
        }
    }

}
Run Code Online (Sandbox Code Playgroud)


Sim*_*olt 12

编辑:编辑原始问题以指示需要运行时解决方案 - 在编辑之前给出了此答案,并且仅指示在开发期间如何执行此操作.

如果您使用的是Eclipse,则可以右键单击该方法并选择"打开调用层次结构"以获取此信息.

阅读评论后更新:其他IDE也以类似的方式支持这一点(至少Netbeans和IntelliJ做)


Rob*_*anu 5

使用@Deprecated注释方法(或使用@deprecated标记),打开弃用警告,运行编译并查看触发的警告.

运行编译位可以通过调用外部ant进程使用Java 6编译器API来完成.


小智 5

  1. 右键单击方法
  2. 转到参考和(根据您的要求)
    选择工作区/项目/层次结构.

这会弹出一个面板,显示对此功能的所有引用.Eclipse FTW!