Найти классы, которые реализуют интерфейсы или являются подклассами / суперклассами в maven CLASSPATH?

Запросы VisualVM OQL не могут запрашивать интерфейсы, поскольку текущий формат дампа кучи не сохраняет эту информацию.

Чтобы обойти эту проблему, можно найти классы, которые реализуют интерфейс и дополнительно выполнить анализ дампа кучи.

У меня есть приложение, управляемое Maven. Во время сборки Maven знаю полное приложение CLASSPATH,

Можно ли сделать запрос через mvn Команда, какие классы в каком пакете реализует выбранный интерфейс?

Или даже больше - найти классы и пакеты в сборке приложения CLASSPATH, которая является подклассами или суперклассами выбранного класса?

Существуют ли плагины, подходящие для моих нужд?

ОБНОВЛЕНИЕ Интересное предложение использовать IDE для получения списка известных реализаций.

Я работаю с Emacs и NetBeans. NETBeans имеют ограниченные возможности (диалогпоиска использования Alt + F7), чтобы найти известную реализацию, но ее область действия ограничена только открытыми проектами. Например я ищу org.hibernate.cfg.NamingStrategy реализация и NetBeans не помогают в моем случае.

Потому что мне нужен этот список для дальнейшего написания сценариев, инструменты GUI не актуальны, если они не обеспечивают чистый экспорт текста.

1 ответ

Если вам действительно нужно добиться этого с помощью maven или сценариев, вот как я это сделал.
Основываясь на подходе, предложенном в другом ответе на Stackru, я реализовал следующий простой класс:

package com.sample;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Scanner;

import org.clapper.util.classutil.ClassFilter;
import org.clapper.util.classutil.ClassFinder;
import org.clapper.util.classutil.ClassInfo;

public class MainScan {

    public static void main(String[] args) throws Exception {
        if (args.length < 2) {
            System.out.println("Missing options");
            System.exit(-1);
        }
        System.out.println("Filtering by: " + args[1]);

        ClassFinder finder = new ClassFinder();
        finder.addClassPath();
        loadClasspath(finder, args[0]);

        ClassFilter filter = new ImplementInterfaceFilter(args[1]);
        // you could also use as a filter: new
        // SubclassClassFilter(AbstractFileFilter.class);
        // or make a concatenation of filters using an AndClassFilter

        Collection<ClassInfo> foundClasses = new ArrayList<ClassInfo>();
        finder.findClasses(foundClasses, filter);

        if (foundClasses.size() > 0) {
            for (ClassInfo classInfo : foundClasses) {
                System.out.println("- " + classInfo.getClassName());
                // consider also using classInfo.getClassLocation() to get the
                // jar file providing it
            }
        } else {
            System.out.println("No matches found.");
        }
    }

    static void loadClasspath(ClassFinder finder, String file) throws IOException {
        Scanner s = new Scanner(new File(file));
        s.useDelimiter(File.pathSeparator);
        try {
            while (s.hasNext()) {
                finder.add(new File(s.next()));
            }
        } finally {
            s.close();
        }
    }

    static class ImplementInterfaceFilter implements ClassFilter {

        private String interfaceName;

        public <T> ImplementInterfaceFilter(String name) {
            this.interfaceName = name;
        }

        public boolean accept(ClassInfo info, ClassFinder finder) {
            for (String i : info.getInterfaces()) {
                if (i.endsWith(this.interfaceName)) {
                    return true;
                }
            }
            return false;
        }

    }
}

Обратите внимание, что класс находится в com.sample пакет, но он, очевидно, может быть перемещен в другой пакет. Основной метод ожидает две опции, файл пути к классам и имя интерфейса, затем он добавит путь к классам в искатель пути к классам и просканирует его, чтобы найти классы, реализующие предоставленное имя интерфейса (через специальный фильтр, также предоставленный выше). Обе опции будут предоставлены во время выполнения Maven следующим образом:

Я использовал эту библиотеку для сканирования пути к классам, поэтому, как указано на официальной странице, нам нужно добавить собственный репозиторий в наше POM:

<repositories>
    <repository>
        <releases>
            <enabled>true</enabled>
            <updatePolicy>always</updatePolicy>
            <checksumPolicy>warn</checksumPolicy>
        </releases>
        <id>clapper-org-maven-repo</id>
        <name>org.clapper Maven Repo</name>
        <url>http://maven.clapper.org/</url>
        <layout>default</layout>
    </repository>
</repositories>

И требуемая зависимость:

<dependencies>
    ...
    <dependency>
        <groupId>org.clapper</groupId>
        <artifactId>javautil</artifactId>
        <version>3.1.2</version>
    </dependency>
    ...
</dependencies>

Тогда нам просто нужно настроить следующее в нашей сборке Maven:

<build>
    <plugins>
        ...
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-dependency-plugin</artifactId>
            <version>2.1</version>
            <executions>
                <execution>
                    <phase>validate</phase>
                    <goals>
                        <goal>build-classpath</goal>
                    </goals>
                    <configuration>
                        <outputFile>${project.build.directory}/classpath.txt</outputFile>
                    </configuration>
                </execution>
            </executions>
        </plugin>
        <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>exec-maven-plugin</artifactId>
            <version>1.1</version>
            <executions>
                <execution>
                    <phase>validate</phase>
                    <goals>
                        <goal>java</goal>
                    </goals>
                    <configuration>
                        <mainClass>com.sample.MainScan</mainClass>
                        <arguments>
                            <argument>${project.build.directory}/classpath.txt</argument>
                            <argument>${interfaceName}</argument>
                        </arguments>
                    </configuration>
                </execution>
            </executions>
        </plugin>
        ...
    </plugins>
</build>

Мы в основном настраиваем плагин зависимостей Maven, чтобы записать полный путь к классу сборки Maven в файл, затем используем плагин Exec Maven для выполнения нашей пользовательской основной Java, передавая ему файл classpath и параметр, ${interfaceName}, Оба исполнения плагинов связаны с validate фаза: нам не нужно выполнять полную сборку maven, мы просто вызовем одну из ее первых фаз для этой задачи.

Таким образом, мы можем вызвать сборку maven следующим образом:

mvn validate -DinterfaceName=Serializable -q

И иметь вывод, подобный следующему:

Filtering by: Serializable
- org.apache.commons.io.ByteOrderMark
- org.apache.commons.io.comparator.CompositeFileComparator
- org.apache.commons.io.comparator.DefaultFileComparator
...

Команда Maven будет напрямую вызывать нашу заинтересованную фазу, validate, с использованием -q возможность (совсем) пропустить любой журнал сборки maven и просто получить интересующий нас вывод. Кроме того, мы можем затем динамически передать нужный интерфейс через -DinterfaceName=<value_here> вариант. Он передаст значение в плагин Exec Maven и, как таковое, в основную часть Java выше.

В соответствии с дальнейшими потребностями (создание сценариев, вывод, форматирование и т. Д.) Основной Java может быть легко адаптирован. Более того, конфигурация плагинов, зависимостей, репозиториев также может быть перемещена в профиль Maven, чтобы сделать его более чистым и организованным.

Последнее замечание: если вы изменили пакет основной Java-программы, не забудьте соответствующим образом изменить конфигурацию плагина Exec Maven (mainClass элемент).

Итак, вернемся к вашим вопросам:

  • Можно ли запросить с помощью команды mvn, какие классы в каком пакете реализует выбранный интерфейс? Да, применяя подход выше.
  • Или даже больше - найти классы и пакеты в сборке приложения CLASSPATH, которая является подклассами или суперклассами выбранного класса? Да, посмотрите на SubclassClassFilter из той же библиотеки, измените главное выше соответствующим образом, и вы получите к нему.
  • Существуют ли плагины, подходящие для моих нужд? Я не смог найти ни одного, но приведенный выше код можно было легко преобразовать в новый плагин Maven. В противном случае описанный здесь подход представляет собой сочетание кода Java и использования существующих плагинов Maven, которые в любом случае могут удовлетворить ваши потребности.
Другие вопросы по тегам