在Maven CLASSPATH中找到实现接口或作为子类/超类的类? [英] Find classes that implement interfaces or being subclasses/superclasses in maven CLASSPATH?

查看:63
本文介绍了在Maven CLASSPATH中找到实现接口或作为子类/超类的类?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

VisualVM OQL查询无法查询接口,因为当前的堆转储格式无法保留此信息.

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

To workaround this issue it is possible to find classes that implements interface and further perform heap dump analysis.

我有一个由Maven管理的应用程序.在构建Maven期间,了解完整的应用程序 CLASSPATH .

I have an application managed by Maven. During build Maven know full application CLASSPATH.

是否可以通过 mvn 命令查询哪个包中的哪些类实现了选定的接口?

Is it possible to query via mvn command which classes in which package implements selected interface?

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

Or even more - to find classes and packages in application build CLASSPATH which is subclasses or superclasses of selected class?

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

Are there exist plug-in suitable for my needs?

更新关于使用IDE获取已知实现列表的有趣建议.

UPDATE Interesting suggestion to use IDE for getting list of known implementation.

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

I work with Emacs and NetBeans. NetBeans have limited ability (Find Usage dialog by Alt+ F7) to find know implementation but its scope is limited to only to open projects. For example I look for org.hibernate.cfg.NamingStrategy implementation and NetBeans doesn't help in my case.

因为我需要该列表以用于其他脚本GUI工具,否则它们将不相关,除非它们提供纯文本导出.

Because I need that list for further scripting GUI tools are not relevant unless they provide clean text export.

推荐答案

如果您确实需要通过maven或脚本来实现此目标,那么这就是我的工作方式.
根据另一个关于Stackoverflow的 answer 建议的方法,我实现了以下简单方法课:

If you really need to achieve this via maven or scripting, here is how I got it working.
Based on the approach suggested by another answer on Stackoverflow, I implemented the following simple class:

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提供,如下所示:

Note, the class is located in the com.sample package, but it can obviously be moved to some other package. The main method expects two options, a classpath file and an interface name, it will then add the classpath to the classpath finder and scan it looking for classes implementing the provided interface name (via a custom filter also provided above). Both options will be provided at runtime by Maven as following:

我使用库进行类路径扫描,因此,根据其官方页面上的建议,我们需要将自定义存储库添加到我们的POM:

I used this library for the classpath scanning, hence as suggested on its official page, we need to add a custom repository to our 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构建中配置以下内容:

Then we just need to configure the following in our Maven build:

<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 main,将类路径文件和参数 $ {传递给它.interfaceName} .这两个插件的执行都链接到 validate 阶段:我们不需要执行完整的Maven构建,我们只需为此任务调用其第一阶段即可.

We are basically configuring the Maven Dependency Plugin to write the full Maven build classpath to a file, then using the Exec Maven Plugin to execute our custom Java main, passing to it the classpath file and a parameter, ${interfaceName}. Both plugins executions are linked to the validate phase: we don't need to execute the full maven build, we will just invoke one of its first phases for this task.

这样,我们可以按以下方式调用Maven构建:

As such, we can invoke the maven build as following:

mvn validate -DinterfaceName=Serializable -q

并具有如下所示的输出:

And have an output like the following:

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

Maven命令将使用 -q 选项(非常)直接调用我们所关注的阶段 validate (跳过),以跳过任何maven构建日志并仅获得感兴趣的输出我们.此外,我们然后可以通过 -DinterfaceName =< value_here> 选项动态传递所需的接口.它将把值传递给Exec Maven插件,并传递给上面的Java main.

The Maven command will directly invoke our concerned phase, validate, using the -q option (quite) to skip any maven build log and just get the output interesting to us. Moreover, we can then dynamically pass the interface we want via the -DinterfaceName=<value_here> option. It will pass the value to the Exec Maven Plugin and as such to the Java main above.

根据进一步的需求(脚本,输出,格式等),可以轻松地修改Java main.此外,插件,依赖项,存储库配置也可以移至Maven配置文件中,以使其更加整洁和井井有条.

According to further needs (scripting, output, format, etc.), the Java main can be easily adapted. Moreover, the plugins, dependency, repositories configuration could also be moved to a Maven profile to have it cleaner and better organized.

最后一点:如果您更改上述Java main的软件包,请不要忘记相应地更改Exec Maven插件配置( mainClass 元素).

Last note: if you change the package of the Java main above, do not forget to change the Exec Maven Plugin configuration accordingly (the mainClass element).

所以,回到您的问题:

  • 是否可以通过mvn命令查询哪个包中的哪个类实现了选定的接口?是的,请采用上述方法.
  • 甚至更多-在应用程序构建CLASSPATH中查找类和包,这些类和包是所选类的子类还是超类?是的,请查看
  • Is it possible to query via mvn command which classes in which package implements selected interface? Yes, applying the approach above.
  • Or even more - to find classes and packages in application build CLASSPATH which is subclasses or superclasses of selected class? Yes, look at the SubclassClassFilter from the same library, change the main above accordingly and you will get to it.
  • Are there exist plug-in suitable for my needs? I couldn't find any, but the code above could be easily converted into a new Maven plugin. Otherwise the approach described here is a mix of Java code and existing Maven plugins usage, which could suit your need anyway.

这篇关于在Maven CLASSPATH中找到实现接口或作为子类/超类的类?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆