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

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

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

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

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

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

Есть ли подходящий плагин для моих нужд?

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

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

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


person gavenkoa    schedule 25.12.2015    source источник
comment
А как насчет вашей IDE?   -  person khmarbaise    schedule 25.12.2015


Ответы (1)


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

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 Dependency для записи полного пути к классам сборки Maven в файл, а затем используем подключаемый модуль Exec Maven для выполнения нашего настраиваемого основного Java-файла, передавая ему файл пути к классам и параметр ${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, что в любом случае может удовлетворить ваши потребности.
person A_Di-Matteo    schedule 01.01.2016