查找实现接口的 Java 类

不久前,我偶然发现了一段代码,它使用了一些标准的 Java 功能来定位实现给定接口的类。我知道这些函数隐藏在一些非逻辑的地方,但是它们可以用于其他类,就像包名所暗示的那样。过去我不需要它,所以我忘记了它,但现在我需要它,而且我似乎找不到它的功能了。在哪里可以找到这些函数?

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

210440 次浏览

不久前,我整理了一个包裹,可以让你做你想做的事,甚至更多。(我正在编写的一个实用程序需要它)。它使用 ASM库。您可以使用反射,但是结果表明 ASM 的性能更好。

我把我的软件包放在我网站上的一个开源库中。图书馆在这里: http://software.clapper.org/javautil/。您需要从具有 课堂搜索类开始。

我为之编写的实用程序是一个 RSS 阅读器,我仍然每天使用它,所以这段代码确实会得到锻炼。我使用 ClassFinder 在 RSS 阅读器中支持一个插件 API; 在启动时,它在一对目录树中查找包含实现某个接口的类的 jar 和类文件。比你想象的要快得多。

这个库是 BSD 许可的,所以您可以安全地将它与您的代码捆绑在一起。

如果这对你有用,请自便。

更新: 如果你正在使用 Scala,你可能会发现 这个图书馆对 Scala 更友好。

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

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

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

只是提醒一下,这些方法和类可能非常慢。

总的来说,这种功能是不可能的。Java类加载器机制只保证能够请求一个具有特定名称(包括包)的类,ClassLoader 可以提供一个类,或者它可以声明它不知道那个类。

类可以(而且经常)从远程服务器加载,它们甚至可以动态构造; 编写一个 ClassLoader 并不困难,它返回一个有效的类,该类实现一个给定的 任何名称接口; 那么实现该接口的类的列表的长度将是无限的。

实际上,最常见的情况是 URLClassLoader在文件系统目录和 JAR 文件的列表中查找类。所以您需要的是获取 URLClassLoader,然后遍历这些目录和存档,对于在这些目录和存档中找到的每个类文件,请求相应的 Class对象并查看返回的 getInterfaces()方法。

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

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

但是,如果您只想知道实现给定接口的类是否已经加载了 事实上,那么这样做会更容易一些:

  • 通过 Java 仪器框架,您可以调用 GetAllLoadedClass ()
  • 通过反射,可以查询给定 ClassLoader 的 ClassLoader 类字段。

如果您使用了插装技术,那么(如链接中所解释的那样)将发生的情况是,当 JVM 启动并传递一个 Instruments 对象时,您的“代理”类基本上将被调用。此时,您可能希望在静态字段中“将其保存为稍后使用”,然后让您的主应用程序代码稍后调用它以获取已加载类的列表。

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

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

包级注释

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

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

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

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

如果人们感兴趣,我将添加更多的细节(代码) ,但大多数可能得到的想法。

MetaInf 服务装入器

要添加到@erickson 回答,还可以使用服务加载器方法。Kohsuke 有一个非常棒的方法来生成服务加载器方法所需的 META-INF 元素:

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

春天可以为你做这些..。

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();

注意: 如果你想要正确的结果,类型过滤器是很重要的! 您也可以在这里使用排除过滤器。

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

我真的很喜欢 反射图书馆这样做。

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

您还可以使用可扩展组件扫描器(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 或应用程序容器中。