查找实现特定接口的所有类

时间:2012-04-03 10:16:00

标签: java java-ee quartz-scheduler

我正在开发一个应用程序(Quartz调度程序),我们有一个工作类负责实际执行工作,我们需要告诉/传递工作类的名称,同时在Quartz中创建一个触发器调度器。

我想为所有想要使用API​​的人提供一个扩展点(除了我将作为API的一部分提供的一些通用作业)。我们的想法是创建一个(标记)接口,如果有人想要将它们的类声明为调度程序作业类,那么他们所要做的就是(声明)实现接口。

我不确定如何找到遵循合同的类(通过实现接口),以便我可以向想要在调度程序中安排触发器的用户显示它们。

我的要求不是在运行时加载类,而是显示实现所需接口的类的用户列表,以便用户可以选择类和类名称可以传递给调度程序。这是Quartz调度程序,它最终将负责创建类的实例。

任何人都可以建议我如何实现上述目标,还是有其他更好的方法来实现我的目标?

修改

我浏览了ServiceLoader的文档,看来为了实现服务,我必须在META-INF文件夹中创建一个带有实现类名称的文件,这让我想到如果我的API的用户需要20个不同的实现,他必须在文件中放入20个条目,这对我来说似乎为最终用户做了很多额外的工作,因为每个工作类都将被创建用于执行特定的工作,并且可以有100个工作类。

如果我的假设错了,请纠正我。

6 个答案:

答案 0 :(得分:33)

您可以找到答案here

我可以建议使用org.reflections

enter link description here

Reflections reflections = new Reflections("com.mycompany");    
Set<Class<? extends MyInterface>> classes = reflections.getSubTypesOf(MyInterface.class);

答案 1 :(得分:32)

我有类似的需求,我想确保创建的任何实现某个接口的类始终是真正可序列化的。我创建了一个JavaClassFinder,它遍历类路径中的所有目录,并找到可分配给我关心的接口的所有类。这是一段代码:

public <T> List<Class<? extends T>> findAllMatchingTypes(Class<T> toFind) {
    foundClasses = new ArrayList<Class<?>>();
    List<Class<? extends T>> returnedClasses = new ArrayList<Class<? extends T>>();
    this.toFind = toFind;
    walkClassPath();
    for (Class<?> clazz : foundClasses) {
        returnedClasses.add((Class<? extends T>) clazz);
    }
    return returnedClasses;
}

如果有帮助,我很乐意与您分享代码。唯一的缺点是这只会处理.class文件 - 我没有添加该功能来解压缩.jars并从那里读取类文件。 (但是添加它不是一个巨大的项目。)

更新:我检查了上面的源代码,发现它依赖于我们标准实用程序库中的很多辅助类。为了方便起见,我压缩了所有需要的代码,您可以从JavaClassFinder.zip下载。这将直接在Eclipse中设置,您可以获取所需代码的任何部分。

您将在项目中找到一个名为JavaClassFinderTest.java的JUnit3测试,它将向您展示JavaClassFinder类的功能和用法。运行Junit测试所需的唯一外部依赖是Junit。

此实用程序的基本用法:

    JavaClassFinder classFinder = new JavaClassFinder();
    List<Class<? extends MyTagInterface>> classes = classFinder.findAllMatchingTypes(MyTagInterface.class);

这将为您提供一个List,其中包含类路径中可从“MyTagInterface.class”(例如)分配的任何类。希望这会有所帮助。

答案 2 :(得分:2)

我认为org.reflections是Alex Stybaev提到的正确解决方案,您不需要在属性文件中引用类。

另一种方法(我会采用)是Spring,因为我仍然使用Spring作为我的应用程序,因此不需要任何额外的依赖。

您可以在此处找到解决Spring问题的提示(以及其他评论或答案中的替代方法):

在你的问题的评论中,heikkim和polypiel也链接到问题,并有答案用Spring解决它:

答案 3 :(得分:1)

使用Java SPI机制执行此操作的最佳(标准)方法可能是Javadoc。不方便(这也是一个很好的功能)是它希望定义扩展的Jars在META-INF/services/your.fully.qualified.Interface中列出它们。

我能想到的另一种方法是通过所有ClassLoaders,希望你能够在那里列出文件,加载类文件,看看它们是否实现了你的接口 - 这不是一件好事。

答案 4 :(得分:0)

Find Java classes implementing an interface的答案中,我使用http://software.clapper.org/javautil/:非常快,非常有用。

答案 5 :(得分:0)

要在纯java中这样做,最正确的答案已经投票了(来自sam goldberg apr 10,2012)

然而,他的完整代码是不必要的复杂。我用他的想法并将ClassFinder推到那里: http://icedtea.classpath.org/hg/icedtea-web/file/0527ad4eb2dd/netx/net/sourceforge/jnlp/controlpanel/ClassFinder.java

注意 - 这将在独立应用程序(或任何使用带有jar和dirs的常规类路径的应用程序)中运行,而不是在ServerContainer中。

如果您的应用在网络服务器中运行,您需要知道您的战争/耳朵的位置并在那里搜索。或者你必须询问你的父母老师他在哪里得到你的(和其他)来源。

第二个注意事项 - 我过滤最终位置类以仅匹配netx和icedtea-web,因为我不会搜索依赖项。因此,如果您需要包含rt.jar,请使用过滤器。或者根本不删除bootclasspath,如果相反的话根本不需要它。