在maven CLASSPATH中查找实现接口或子类/超类的类?

时间:2015-12-25 12:32:43

标签: maven classpath maven-plugin visualvm oql

VisualVM OQL queries can't query for interfaces because current heap dump format doesn't preserve this info

要解决此问题,可以找到实现接口的类并进一步执行堆转储分析。

我有一个由Maven管理的应用程序。在构建期间,Maven知道完整的应用程序CLASSPATH

是否可以通过mvn命令查询哪个类实现了所选接口?

甚至更多 - 在应用程序构建CLASSPATH中查找类和包,它是所选类的子类或超类?

是否存在适合我需要的插件?

更新使用IDE获取已知实施列表的有趣建议。

我使用Emacs和NetBeans。 NetBeans的能力有限(查找用法对话 Alt + F7 )以查找已知的实现,但其范围仅限于打开项目。例如,我寻找org.hibernate.cfg.NamingStrategy实现,NetBeans对我的情况没有帮助。

因为我需要该列表用于进一步的脚本GUI工具不相关,除非它们提供干净的文本导出。

1 个答案:

答案 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元素)。

所以,回到你的问题:

  • 是否可以通过mvn命令查询哪个类实现了所选接口?是,应用上述方法。
  • 甚至更多 - 在应用程序构建CLASSPATH中找到类和包,它是所选类的子类或超类?是的,查看同一个库中的SubclassClassFilter,更改主相应地,你会得到它。
  • 是否存在适合我需要的插件?我找不到任何插件,但上面的代码可以很容易地转换为新的Maven插件。否则,此处描述的方法是Java代码和现有Maven插件使用的混合,无论如何都可以满足您的需求。