【问题标题】:Find Java classes implementing an interface [duplicate]查找实现接口的Java类[重复]
【发布时间】:2010-09-30 22:57:27
【问题描述】:

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

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

【问题讨论】:

标签: java interface


【解决方案1】:

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

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

我为它编写的实用程序是一个 RSS 阅读器,我仍然每天都在使用它,因此代码确实会得到锻炼。我使用 ClassFinder 来支持 RSS 阅读器中的插件 API;在启动时,它会在几个目录树中查找包含实现某个接口的类的 jar 和类文件。它比您预期的要快得多。

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

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

更新:如果您使用的是 Scala,您可能会发现 this library 对 Scala 更友好。

【讨论】:

  • Brian,这也适用于接口继承吗?在我的项目中,我有一个接口作为所有插件的模板,然后几个接口扩展了该接口以提供专门的插件类型。我是否能够使用父类型加载所有插件,还是必须遍历所有子类型才能获取我的所有插件?
  • 应该的。如果没有,我需要修复它...
  • 布赖恩,非常有帮助。它是否支持目录参数,以便我可以在库中找到类。
  • 是的。您将目录添加到查找器。它还会搜索 jars。
  • @Brian 您使用 ASM 的事实是否意味着类实际上并没有被加载,而只是它们自己的文件被扫描?由于将所有内容从类路径加载到内存以进行扫描,这将是一个巨大的开销。
【解决方案2】:

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

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

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

【讨论】:

  • 您可能需要添加s.setIncludeAnnotationConfig(false); 以过滤掉 Spring 自己的类。
  • 但是String[] 有什么用呢?我们这里需要的是CLASS_YOU_WANTclasses 的列表,
  • 您可以使用 bean 名称从 BeanDefinitionRegistry 实例中获取 BeanDefinition
  • s.resetFilters(false); 将删除默认的 Spring 过滤器,这些过滤器会将所有带有@Component、@Service 和一些其他注释的类包含到结果集中。
  • 您可以使用ClassPathScanningCandidateComponentProvider直接获取BeanDefinition。构造函数采用一个布尔值,允许您禁用拾取@Component@Service 等的默认过滤器。TypeFilter 可以包含使用addIncludeFilter 和通过findCandidateComponents("com.my.package") 检索的定义
【解决方案3】:

我真的很喜欢 reflections library 这样做。

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

【讨论】:

  • 我也喜欢反射库
【解决方案4】:

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

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

【讨论】:

  • AFAI 明白了,发帖者确实想把这个用于任意接口,所以 ServiceLoader 可能没有帮助。
  • 如果您想自动生成 META-INF 服务,请查看 Kohsuke 的 MetaInfServices weblogs.java.net/blog/kohsuke/archive/2009/03/…
  • ServiceLoader 似乎无法找到任意接口的实现者。
  • 我喜欢这个答案 1)它是一个标准的 java 工具,2)我正在定义一个可扩展的框架,所以这是完美的。不同的人不同的笔画,这恰好是我的笔画!
  • @drew 这在 Java 9 中发生了变化。它现在提供了一种无需实例化即可遍历所有提供程序的方法,因此您可以先查询Class。此外,对于使用模块的 Java 代码,现在可以通过静态工厂方法提供服务,this answer 提供了一个集成 enum 类型的示例。
【解决方案5】:

包级注释

我知道这个问题很久以前就已经回答过了,但是解决这个问题的另一种方法是使用 Package Level Annotations。

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

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

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

如果人们感兴趣,我会添加更多细节(代码),但很可能会明白。

MetaInf 服务加载器

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

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

【讨论】:

  • 当心:并非所有软件包在运行时都可用。请参阅here 了解更多信息。
  • 请添加更多代码以显示注释示例。已经 +1 了。
  • @clafonta 我相信通过在该类中使用 package-info.java 和注释,包将很早就被加载(渴望)。还有其他人使用这种包信息注释技术(JAX-RS 和 JAXB)(stackoverflow.com/questions/8735737/…)。基本上,您需要依赖首先在这些包中加载的其他内容。
【解决方案6】:

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

【讨论】:

  • 这是迄今为止收集实现类的最简单方法。
  • 这似乎仅限于在特定包中搜索,不是吗?
  • 这是我能找到的唯一解决方案,它也可以处理所有子接口的实现类。它易于集成和使用。
  • 看起来这可能与现代版本的 Java 不兼容(为 Java 8 抛出了例外)
  • 好吧,它仍然可以与 Java 8 一起使用,即使如果您安装了记录器,则会引发并记录异常。对于 Java 9 及更高版本,我没有尝试过,但如果它仍然有效,我会感到非常惊讶,因为 Jigsaw 项目的影响。
【解决方案7】:

总的来说,这个功能是不可能的。 Java ClassLoader 机制只保证能够请求具有特定名称(包括 pacakge)的类,并且 ClassLoader 可以提供一个类,也可以声明它不知道该类。

类可以(并且经常)从远程服务器加载,甚至可以动态构建;编写一个返回有效类的 ClassLoader 一点也不难,该类为您要求的 any 名称实现给定接口;实现该接口的类列表的长度将是无限的。

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

【讨论】:

    【解决方案8】:

    很明显,Class.isAssignableFrom() 告诉你一个individual 类是否实现了给定的接口。那么问题就在于获取要测试的类列表。

    据我所知,Java 无法直接向类加载器询问“您可以可能加载的类的列表”。因此,您必须自己执行此操作,方法是遍历可见的 jar,调用 Class.forName() 来加载类,然后对其进行测试。

    但是,如果您只是想从那些 实际 已加载的类中了解实现给定接口的类,这会更容易一些:

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

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

    【讨论】:

    • Instrumentation.getAllLoadedClasses() :无法从 Instrumentation 类型对非静态方法 getAllLoadedClasses() 进行静态引用 .................. Java 8
    • 将 Instrumentation 实例传递给 premain 方法: public static void premain( String agentArgs, Instrumentation instrumentation )
    【解决方案9】:

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

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

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

    只是一个警告,这些方法和类可能会很慢。

    【讨论】:

      猜你喜欢
      • 2010-12-17
      • 2012-04-16
      • 1970-01-01
      • 2012-11-27
      • 2011-08-02
      • 2014-04-05
      • 1970-01-01
      • 2023-03-03
      • 1970-01-01
      相关资源
      最近更新 更多