Найти классы, которые реализуют интерфейсы или являются подклассами / суперклассами в maven CLASSPATH?
Чтобы обойти эту проблему, можно найти классы, которые реализуют интерфейс и дополнительно выполнить анализ дампа кучи.
У меня есть приложение, управляемое 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, которые в любом случае могут удовлетворить ваши потребности.