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

如何在Java中找到所有调用给定方法的方法?,java,Java,我需要为我感兴趣的Java方法获取所有调用方方法的列表。有没有工具可以帮我解决这个问题 编辑:我忘了提到我需要从程序中执行此操作。我正在使用Java Pathfinder,我想在所有调用我感兴趣的方法的方法中运行它。编辑:编辑原始问题是为了表明需要运行时解决方案-此答案在编辑之前给出,仅说明如何在开发期间执行 如果您使用的是Eclipse,则可以右键单击该方法并选择“打开调用层次结构”来获取此信息 阅读评论后更新:其他IDE也以类似的方式支持此功能(至少是Netbeans和IntelliJ-do

我需要为我感兴趣的Java方法获取所有调用方方法的列表。有没有工具可以帮我解决这个问题


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

编辑:编辑原始问题是为了表明需要运行时解决方案-此答案在编辑之前给出,仅说明如何在开发期间执行

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


阅读评论后更新:其他IDE也以类似的方式支持此功能(至少是Netbeans和IntelliJ-do)

是的,大多数现代IDE:s将允许您搜索方法或变量的用法。或者,您可以使用调试器并在方法项上设置跟踪点,每次调用该方法时打印堆栈跟踪或其他内容。 最后,您可以使用一些简单的shell util对方法进行grep,例如

find . -name '*.java' -exec grep -H methodName {} ;

不过,唯一可以让您找到通过某种反射方法进行的调用的方法是使用调试器。

我能找到的最接近的方法是这个StackOverflow问题选择答案中描述的方法。

您可以在IDE中使用一些东西,例如“查找用法”(这就是Netbeans和JDeveloper中的名称)。需要注意以下几点:

  • 如果您的方法实现了来自接口或基类的方法,那么您只能知道您的方法可能被调用
  • 很多Java框架都使用反射来调用您的方法(如Spring、Hibernate、JSF等),所以要小心
  • 同样,您的方法可能会被某个框架调用,反射式调用或非反射式调用,因此请再次小心

  • 没有办法通过Java反射库(以编程方式)实现这一点——您不能问“您调用了哪些方法?”

    这就留下了我能想到的另外两个选择:

  • 源代码的静态分析。我确信Eclipse Java工具集就是这样做的——您可以查看JDT背后的Eclipse源代码,并找到当您要求Eclipse“查找引用”方法时它所做的事情

  • 字节码分析。你可以检查字节码中的方法调用。我不确定有哪些库或示例可以帮助你做到这一点,但我无法想象有什么东西不存在


  • 用@Deprecated注释该方法(或用@Deprecated标记),打开不推荐使用警告,运行编译并查看触发了哪些警告


    编译位的运行可以通过调用外部ant进程或使用。

    分析字节码来完成,我建议。给定要分析的类列表,可以让访问者查找您感兴趣的方法调用。下面是一个分析jar文件中类的实现

    请注意,ASM使用with“/”而不是“.”作为分隔符。请将目标方法指定为不带修饰符的

    例如,要在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)"
    
    编辑:添加了源代码和行号:请注意,这只表示每个调用方法的最后一次目标方法调用-原始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
    
    资料来源:

    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();
            }
        }
    
    }
    
    公共类应用程序{
    私有字符串targetClass;
    私有方法targetMethod;
    个人简历;
    私有ArrayList被调用方=新ArrayList();
    私有静态类被调用方{
    字符串类名称;
    字符串方法名;
    字符串methodDesc;
    字符串源;
    内线;
    公共被调用方(字符串cName、字符串mName、字符串mDesc、字符串src、整数ln){
    className=cName;methodName=mName;methodDesc=mDesc;source=src;line=ln;
    }
    }
    私有类AppMethodVisitor扩展MethodAdapter{
    布尔callsTarget;
    内线;
    公共AppMethodVisitor(){super(新的EmptyVisitor());}
    public void visitMethodInsn(int操作码、字符串所有者、字符串名称、字符串描述){
    if(所有者等于(targetClass)
    &&name.equals(targetMethod.getName())
    &&desc.equals(targetMethod.getDescriptor()){
    callsTarget=true;
    }
    }
    公共无效访问代码(){
    callsTarget=false;
    }
    公共无效visitLineNumber(内线,标签开始){
    this.line=line;
    }
    公共图书馆{
    if(callsTarget)
    添加(新被调用方(cv.className、cv.methodName、cv.methodDesc、,
    cv.来源、行);
    }
    }
    私有类AppClassVisitor扩展类适配器{
    私有AppMethodVisitor mv=新的AppMethodVisitor();
    公共字符串源;
    公共字符串类名;
    公共字符串方法名;
    公共字符串methodDesc;
    公共AppClassVisitor(){super(新的EmptyVisitor());}
    公共无效访问(int版本、int访问、字符串名称、,
    字符串签名、字符串超名、字符串[]接口){
    className=名称;
    }
    public void visitSource(字符串源、字符串调试){
    this.source=源;
    }
    public方法访问者访问方法(int访问,字符串名称,
    字符串描述,字符串签名,
    字符串[]异常){
    methodName=name;
    methodDesc=desc;
    返回mv;
    }
    }
    公共无效查询
    
    /**
     * 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();
    }