查找实现接口的Java类

时间:2009-01-12 16:05:54

标签: java interface

前一段时间,我遇到了一段代码,它使用了一些标准的Java功能来定位实现给定接口的类。我知道函数隐藏在某些非逻辑位置,但它们可以用于其他类,因为包含的名称是隐含的。那时我不需要它,所以我忘了它,但现在我做了,我似乎无法再找到这些功能。哪些功能可以找到?

编辑:我不是在寻找任何IDE函数或任何东西,而是可以在Java应用程序中执行的东西。

9 个答案:

答案 0 :(得分:62)

前一段时间,我整理了一个包,用于做你想做的事情,等等。 (我需要它用于我正在编写的实用程序)。它使用ASM库。你可以使用反射,但ASM表现更好。

我将我的包放在我网站上的开源库中。图书馆在这里:http://software.clapper.org/javautil/。您想从with ClassFinder类开始。

我为它编写的实用程序是一个RSS阅读器,我仍然每天使用它,所以代码确实倾向于运用。我使用ClassFinder来支持RSS阅读器中的插件API;在启动时,它在几个目录树中查找包含实现特定接口的类的jar和类文件。它比你想象的要快得多。

该库是BSD许可的,因此您可以安全地将其与您的代码捆绑在一起。来源可用。

如果这对你有用,请自己帮忙。

更新:如果你正在使用Scala,你可能会发现this library更加适合Scala。

答案 1 :(得分:48)

Spring可以为你做到这一点......

BeanDefinitionRegistry bdr = new SimpleBeanDefinitionRegistry();
ClassPathBeanDefinitionScanner s = new ClassPathBeanDefinitionScanner(bdr);

TypeFilter tf = new AssignableTypeFilter(CLASS_YOU_WANT.class);
s.addIncludeFilter(tf);
s.scan("package.you.want1", "package.you.want2");       
String[] beans = bdr.getBeanDefinitionNames();

N.B。如果您想要正确的结果,TypeFilter很重要! 您也可以在此处使用排除过滤器。

Scanner可以在spring-context jar中找到,spring-beans中的注册表,类型过滤器是spring-core。

答案 2 :(得分:28)

我非常喜欢reflections library这样做。

它提供了许多不同类型的扫描程序(getTypesAnnotatedWithgetSubTypesOf等),编写或扩展自己的扫描程序非常简单。

答案 3 :(得分:22)

您所谈论的代码听起来像ServiceLoader,,它是在Java 6中引入的,用于支持自Java 1.3或更早版本以来定义的功能。出于性能原因,这是在运行时查找接口实现的推荐方法;如果您需要在旧版本的Java中支持此功能,我希望您会发现my implementation有用。

在早期版本的Java中有一些这样的实现,但是在Sun软件包中,而不是在核心API中(我认为ImageIO内部有一些类来执行此操作)。由于代码很简单,我建议您提供自己的实现,而不是依赖于可能发生变化的非标准Sun代码。

答案 4 :(得分:13)

套餐级别注释

我知道很久以前这个问题已经得到解答了,但是这个问题的另一个解决方案是使用包级别注释。

虽然很难找到JVM中的所有类,但实际上很容易浏览包层次结构。

Package[] ps = Package.getPackages();
for (Package p : ps) {
  MyAno a = p.getAnnotation(MyAno.class)
  // Recursively descend
}

然后让你的注释有一个Class数组的参数。 然后在package-info.java中为特定包放置MyAno。

如果人们有兴趣,我会添加更多细节(代码),但最有可能得到这个想法。

MetaInf服务加载程序

要添加到@erickson答案,您还可以使用服务加载器方法。 Kohsuke有一种很棒的方式来生成服务加载器方法所需的META-INF内容:

http://weblogs.java.net/blog/kohsuke/archive/2009/03/my_project_of_t.html

答案 5 :(得分:8)

您还可以使用可扩展组件扫描程序(extcos:http://sf.net/projects/extcos)并搜索所有实现如下界面的类:

Set<Class<? extends MyInterface>> classes = new HashSet<Class<? extends MyInterface>>();

ComponentScanner scanner = new ComponentScanner();
scanner.getClasses(new ComponentQuery() {
    @Override
    protected void query() {
        select().
        from("my.package1", "my.package2").
        andStore(thoseImplementing(MyInterface.class).into(classes)).
        returning(none());
    }
});

这适用于文件系统上的类,在jar中,甚至适用于JBoss虚拟文件系统上的类。它进一步设计为在独立应用程序以及任何Web或应用程序容器中工作。

答案 6 :(得分:6)

完全一般,此功能是不可能的。 Java ClassLoader机制仅保证能够请求具有特定名称的类(包括pacakge),并且ClassLoader可以提供类,或者它可以声明它不知道该类。

可以(并且经常)从远程服务器加载类,甚至可以在运行中构建它们;编写一个ClassLoader并不难,它返回一个有效的类,该类实现了你从中提出的任何名称的给定接口;实现该接口的类的列表的长度将是无限的。

实际上,最常见的情况是URLClassLoader,它在文件系统目录和JAR文件列表中查找类。因此,您需要获取URLClassLoader,然后遍历这些目录和存档,并为您在其中找到的每个类文件请求相应的Class对象并查看其{{1}的返回值。 1}}方法。

答案 7 :(得分:5)

显然,Class.isAssignableFrom()会告诉您个人类是否实现了给定的接口。那么问题是获得要测试的类列表。

据我所知,Java没有直接的方式向类加载器询问“你可能可能加载”的类列表。所以你必须通过迭代可见的jar来自己做,调用Class.forName()来加载类,然后测试它。

但是,如果你只是想知道实际 em 已加载的那些实现给定接口的类,那会更容易一些:

  • 通过Java Instrumentation框架,您可以调用 Instrumentation.getAllLoadedClasses()
  • 通过反射,您可以查询给定ClassLoader的 ClassLoader.classes 字段。

如果您使用检测技术,那么(如链接中所述)会发生什么是在JVM启动时调用“代理”类并传递Instrumentation对象。此时,您可能希望在静态字段中“将其保存以供日后使用”,然后让主应用程序代码稍后调用它以获取已加载类的列表。

答案 8 :(得分:0)

如果你从使用正在运行的程序解决问题的角度问,那么你需要查看java.lang。*包。如果你得到一个Class对象,你可以使用isAssignableFrom方法来检查它是否是另一个Class的接口。

没有一种简单的内置搜索方式,像Eclipse这样的工具构建了这些信息的索引。

如果没有要测试的Class对象的特定列表,可以查看ClassLoader对象,使用getPackages()方法并构建自己的包层次结构迭代器。

虽然这些方法和类可能很慢,但只是警告。