要解决此问题,可以找到实现接口的类并进一步执行堆转储分析。
我有一个由Maven管理的应用程序。在构建期间,Maven知道完整的应用程序CLASSPATH
。
是否可以通过mvn
命令查询哪个类实现了所选接口?
甚至更多 - 在应用程序构建CLASSPATH中查找类和包,它是所选类的子类或超类?
是否存在适合我需要的插件?
更新使用IDE获取已知实施列表的有趣建议。
我使用Emacs和NetBeans。 NetBeans的能力有限(查找用法对话 Alt + F7 )以查找已知的实现,但其范围仅限于打开项目。例如,我寻找org.hibernate.cfg.NamingStrategy
实现,NetBeans对我的情况没有帮助。
因为我需要该列表用于进一步的脚本GUI工具不相关,除非它们提供干净的文本导出。
答案 0 :(得分:2)
如果你真的需要通过maven或脚本来实现这一目标,那么我就是这样做的 根据Stackoverflow上另一个answer建议的方法,我实现了以下简单类:
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
包中,但显然可以移动到其他包中。 main方法需要两个选项,一个类路径文件和一个接口名称,然后将类路径添加到类路径查找程序并扫描它,查找实现提供的接口名称的类(通过上面提供的自定义过滤器)。 Maven将在运行时提供这两个选项,如下所示:
我使用this库进行类路径扫描,因此正如其官方页面上所建议的那样,我们需要为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 Plugin将完整的Maven构建类路径写入文件,然后使用Exec Maven插件执行我们的自定义Java main,并将类路径文件和参数${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 main。
根据进一步的需求(脚本,输出,格式等),可以轻松地调整Java main。此外,插件,依赖项,存储库配置也可以移动到Maven配置文件,以使其更清晰,更有条理。
最后注意:如果您更改上面Java main的包,请不要忘记相应地更改Exec Maven插件配置(mainClass
元素)。
所以,回到你的问题: