2012-04-03 92 views
26

我正在开发一个应用程序(Quartz scheduler),我们有一个工作类负责实际执行工作,我们需要告知/传递作业类的名称同时在Quartz调度器中创建一个触发器。查找所有实现特定接口的类

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

我不知道如何找到哪些类遵循合同(通过实现接口),以便我可以将它们展示给希望在调度程序中调度触发器的用户。

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

任何人都可以建议我如何实现上述目标,或者是否有其他更好的方法来实现我想要做的事情?

编辑

我通过ServiceLoader的文档去了,似乎是实现服务一个具有创建在META-INF文件夹中的文件,实现类的名字,这使我认为如果我的API的用户需要20个不同的实现,他必须在文件中放入20个条目,这对我来说似乎对最终用户来说是很多额外的工作,因为每个工作类将被创建用于执行特定工作,并且可以成为100个工作班。

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

+2

另一个尝试是创建一个注释,然后搜索注释的类。这里http://stackoverflow.com/questions/259140/scanning-java-annotations-at-runtime说如何实现它。 – polypiel 2012-04-03 10:21:43

+0

这会有帮助吗? [链接](http://www.javaworld.com/javaworld/javaqa/2003-07/02-qa-0725-classsrc2.html?page=1) – titogeo 2012-04-12 09:20:04

回答

30

我也有类似的需要,我想请确保实现某个接口创建的任何类总是真正的序列化。我创建了一个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); 

这会给你包含在classpath这是从“MyTagInterface.class”(例如)分配的任何类的列表。希望这可以帮助。

+0

缺乏“walkClassPath”方法。你可以提供吗? – 2012-04-11 12:45:16

+0

@YvesMartin:Yves,我更新了我的答案,包括所有链接以下载原始代码。它可能比你想要的要多,但它是一个完整的工作版本。 – 2012-04-11 15:32:34

+2

@SamGoldberg你在这里设计的优秀实用工具。 – shashankaholic 2012-04-12 09:33:55

0

可能是使用Java SPI机制完成此操作的最佳(标准)方法,请参阅Javadoc。不方便(这也是一个不错的功能)是,它期望Jars定义扩展名以在META-INF/services/your.fully.qualified.Interface中列出它们。

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

27

您可以在这里找到答案here

我可以建议使用org.reflections

enter link description here

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

感谢您的输入,我正在通过ServiceLocator文件,它似乎对于我的接口(服务)的每个实现,最终用户必须将该条目放在文件中才能加载,这就好像开发人员希望他/她需要20个这样的实现来将所有这些实用名称放在文件,对于最终用户来说似乎很多工作。 – 2012-04-03 11:43:50

1

我认为org.reflections是由亚历克斯Stybaev提到一个妥善的解决办法,你并不需要引用在属性文件中的类。

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

你可以在这里找到(在其他意见或答案,备选方案),与春天解决您的问题提示:

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

0

要在纯java做到这一点,最正确的答案是已经投(萨姆·戈德堡四月10,2012)

他是过于复杂,但是完整的代码。我使用他的想法,并推动了ClassFinder: http://icedtea.classpath.org/hg/icedtea-web/file/0527ad4eb2dd/netx/net/sourceforge/jnlp/controlpanel/ClassFinder.java

注意 - 这将在独立的应用程序(或任何使用普通类路径与罐和dirs)不在ServerContainer中工作。

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

第二个注意事项 - 我正在过滤最终位置类,只匹配netx和icedtea-web,因为我不会搜索依赖关系。所以如果你需要包含rt.jar,请使用这些过滤器。或者,如果你相反根本不需要bootclasspath的话。

相关问题