Как я могу найти все методы, которые вызывают данный метод в Java? - PullRequest
31 голосов
/ 30 мая 2009

Мне нужно получить список всех методов вызова для интересующего меня метода в Java. Есть ли инструмент, который может мне помочь с этим?

Edit: я забыл упомянуть, что мне нужно сделать это из программы. Я использую Java Pathfinder и хочу запустить все методы, которые вызывают мой интересующий метод.

Ответы [ 11 ]

43 голосов
/ 31 мая 2009

Для анализа байт-кода я бы рекомендовал ASM . Имея список анализируемых классов, можно создать посетителя, который найдет интересующие вас вызовы методов. Ниже приведена одна реализация, которая анализирует классы в jar-файле.

Обратите внимание, что ASM использует internalNames с '/' вместо '.' в качестве разделителя. Укажите целевой метод в виде стандартного объявления без модификаторов.

Например, чтобы перечислить методы, которые могут вызывать System.out.println ("foo") в jar времени выполнения java:

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

}
12 голосов
/ 30 мая 2009

Изменить: исходный вопрос был отредактирован, чтобы указать, что было необходимо решение во время выполнения - этот ответ был дан перед этим редактированием и указывает только, как это сделать во время разработки.

Если вы используете Eclipse, вы можете щелкнуть правой кнопкой мыши по методу и выбрать «Открыть иерархию вызовов», чтобы получить эту информацию.

Обновлено после прочтения комментариев: другие IDE также поддерживают это аналогичным образом (по крайней мере, Netbeans и IntelliJ)

5 голосов
/ 26 августа 2011
  1. щелкните правой кнопкой мыши на методе
  2. Перейти к ссылкам и (в зависимости от ваших требований)
    выберите рабочее пространство / проект / иерархию.

Появляется панель, которая показывает все ссылки на эти функции. Затмение FTW!

5 голосов
/ 31 мая 2009

Аннотируйте метод с помощью @Deprecated (или пометьте его как @deprecated), включайте предупреждения об устаревании, запускайте компиляцию и смотрите, какие предупреждения срабатывают.

Запуск вашего бита компиляции можно выполнить, вызвав внешний процесс ant или , используя API компилятора Java 6 .

4 голосов
/ 31 мая 2009

В затмении выделите имя метода, а затем Ctrl + Shift + G

3 голосов
/ 31 мая 2009

Нет способа сделать это (программно) с помощью библиотек отражений Java - вы не можете спросить java.lang.reflect.Method"какие методы вы вызываете?"

Это оставляет два других варианта, которые я могу придумать:

  1. Статический анализ исходного кода. Я уверен, что это именно то, что делает набор инструментов Eclipse Java - вы можете посмотреть на источник Eclipse, стоящий за JDT, и выяснить, что он делает, когда попросите Eclipse «Найти ссылки» на метод.

  2. Анализ байт-кода. Вы можете проверить байт-код для вызовов метода. Я не уверен, какие библиотеки или примеры существуют, чтобы помочь с этим - но я не могу вообразить, что что-то не существует.

1 голос
/ 30 августа 2016

1) В Eclipse это -> щелкните правой кнопкой мыши на методе и выберите открытую иерархию вызовов или CLT+ALT+H

2) В jdeveloper это -> щелчок правой кнопкой мыши на методе и выбор вызовов или ALT+SHIFT+H

1 голос
/ 30 мая 2009

Да, большинство современных IDE позволяют вам либо искать использование метода, либо переменной. В качестве альтернативы вы можете использовать отладчик и установить точку трассировки в записи метода, распечатывая трассировку стека или что-либо еще при каждом вызове метода. Наконец, вы можете использовать несколько простых утилит оболочки, чтобы просто использовать grep для метода, например

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

Единственный метод, который позволит вам найти вызовы, сделанные с помощью некоторого метода отражения, - это использование отладчика.

0 голосов
/ 16 апреля 2014

Я сделал небольшой пример, используя пример @ Чедвика. Это тест, который оценивает, выполняются ли вызовы 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();
}
0 голосов
/ 30 мая 2009

Вы можете сделать это с помощью чего-то в вашей среде IDE, например «Найти использование» (так оно называется в Netbeans и JDeveloper). Несколько вещей на заметку:

  1. Если ваш метод реализует метод из интерфейса или базового класса, вы можете только знать, что ваш метод ВОЗМОЖНО вызван.
  2. Многие Java-фреймворки используют Reflection для вызова вашего метода (IE Spring, Hibernate, JSF и т. Д.), Поэтому будьте осторожны с этим.
  3. В том же примечании ваш метод может вызываться каким-то фреймворком, рефлексивно или нет, поэтому снова будьте осторожны.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...