【问题标题】:Find classes implementing an interface in Jar在 Jar 中查找实现接口的类
【发布时间】:2013-05-14 14:52:26
【问题描述】:

我想查看 jar 中的类是否实现了特定的接口。我已经实现了下面的代码,但是它遍历了 jar 文件中的所有类,并在每个类上查找它是否实现了这个特定的接口。

    public static synchronized boolean findClassesInJar(final Class<?> baseInterface, final String jarName){
        final List<String> classesTobeReturned = new ArrayList<String>();
        if (!StringUtils.isBlank(jarName)) {
            //jarName is relative location of jar wrt. 
            final String jarFullPath = File.separator + jarName;
            final ClassLoader classLoader = this.getClassLoader();
            JarInputStream jarFile = null;
            URLClassLoader ucl = null;
            final URL url = new URL("jar:file:" + jarFullPath + "!/");
            ucl = new URLClassLoader(new URL[] { url }, classLoader);
            jarFile = new JarInputStream(new FileInputStream(jarFullPath));
            JarEntry jarEntry;
            while (true) {
                jarEntry = jarFile.getNextJarEntry();
                if (jarEntry == null)
                    break;
                if (jarEntry.getName().endsWith(".class")) {
                    String classname = jarEntry.getName().replaceAll("/", "\\.");
                    classname = classname.substring(0, classname.length() - 6);
                    if (!classname.contains("$")) {
                        try {
                            final Class<?> myLoadedClass = Class.forName(classname, true, ucl);
                            if (baseInterface.isAssignableFrom(myLoadedClass)) {
                                return true;
                            }
                        } catch (final ClassNotFoundException e) {

                        } 
                    }
                }
            }
        return false;
    }

有什么简单的方法可以做到这一点吗?因为如果有一个 jar 有 100 个类文件并且第 100 个类实现了这个接口,通过上面的代码我需要遍历所有 100 个类文件,看看它是否实现了接口。有什么有效的方法吗?

【问题讨论】:

标签: java interface jar


【解决方案1】:

Reflections 库可以做到这一点:

Reflections reflections = new Reflections(
    ClasspathHelper.forPackage("your.root.package"), new SubTypesScanner());
Set<Class<? extends YourInterface>> implementingTypes =
     reflections.getSubTypesOf(YourInterface.class);

【讨论】:

  • 我对这个问题的理解是,OP 正在寻找一种比 O(n) 运行得更好的方法。我不确定 Reflections 库是如何实现的,但仅从这段代码的外观来看,我认为它的效率低于 OP 的当前代码。
  • @Vulcan - OP 提出 2 个问题 - 一个更简单的解决方案和一个更有效的解决方案。这肯定更简单。
  • @Vulcan a)我在问题的任何地方都找不到这个假设 b)反射是一个稳定的库,在生产中经常使用。它和这样的库一样高效
  • @Vulcan 一方面,OP 实际上会加载所有类,而任何合理的库(包括反射)只会检查类元数据
  • @Durandal 一个类是一个定义好的格式的二进制资源,不需要实际加载一个类来读取该格式,实际上不加载所有类要快很多.
【解决方案2】:

Spring 在ClassPathScanningCandidateComponentProvider 中有一些帮助代码,但它基本上完成了你所做的工作。

您可能还想查看Freud,这是一个舒适的工具包,可以编写静态分析测试,例如确保某个包中的所有类或实现某个接口的类也实现equals()hashCode()

弗洛伊德的好处是它可以分析Java 源代码和类文件(即源代码和编译的字节码),它可以查看属性或文本文件,它可以读取CSS和Spring配置(因此它可以确保DA​​O bean的所有重要方法都有@Transactional)。

【讨论】:

  • "Spring 在 ClassPathScanningCandidateComponentProvider 中为此提供了一些帮助代码,但它基本上可以完成您所做的工作" 不,它没有。 Spring 使用 ASM 读取类元数据,而不是实际加载类。 (这就是我为此建议 +1 的原因)
  • 我的意思是 Spring 还从类路径上的 JAR 文件中读取所有条目;主要区别在于它使用ClassLoader.getResources() 来执行此操作,而不是打开 JAR 并遍历条目。
【解决方案3】:

一般来说,没有简单的方法。甚至没有获取该信息的通用方法。

这里的问题在于 Java 的类加载机制。在实际加载某个类之前,您无法判断它是否实现了接口。如果不尝试加载,您无法判断一个类是否存在(请注意,您甚至无法判断哪些类在类路径上可用)。

这里的原因很简单,类加载器不提供任何枚举类功能,原因是类路径可能无限大/深,并且检索该枚举的成本(及时)可能是无限的(想想一个将您连接到大型远程代码存储库的 URL 类加载器)。

所以你的方法已经可以做到最好了。

【讨论】:

  • “在你实际加载一个类之前,你无法判断它是否实现了一个接口。”那根本不正确。如果是这种情况,许多现代框架(如 Spring)就无法正常工作。 Spring 使用ASM 读取MetadataReaderFactory 中的类元数据,并且不会在该步骤中加载类。我在答案中链接到的反射库也可以这样工作。
  • 虽然它可能不需要将它们加载到VM中,但它仍然需要加载类文件。 Spring 不能在不实际分析类结构的情况下神奇地判断一个类是否实现了接口,同意吗?所以这里的 load 是模棱两可的。没错,我的意思是“通过类加载器加载”;但是严格来说来说,类资源只有在被类加载器加载时才成为类,所以它可以被解释为任何一种方式。我们现在可以无休止地挑剔 load == read 与否。您的评论可以被解释为自相矛盾。
  • 明智的做法是先读取元数据,判断类是否需要加载,然后加载,所以实际上涉及两个“加载”步骤:分析类并通过ClassLoader加载.区别很重要,原因如下:a)通过库(如 asm)检查的资源可以被垃圾收集,类一旦加载就不能。 b) 类可以引用可选的依赖项,即可能在也可能不在类路径上的类。加载这样的类将触发异常,并且 c) 该类可能包含静态初始化程序中的代码
  • 通过类加载器加载的类也可以被垃圾收集,我很惊讶你声称它们不能被收集(见:stackoverflow.com/questions/2433261/…)。而且,如果您在加载类时遇到任何异常,那么当您稍后尝试实际使用它时也会遇到该异常(为什么您仍然暗示这是一件坏事?)。如果您真的想对找到的类某些事情(无论如何您都会加载它们),那么首先加载为“元数据”是浪费时间和资源。
  • 重新垃圾收集类我站得更正,谢谢。其他一切我仍然不同意。没有办法我会加载类路径上的每个类(并运行它们所有可能邪恶的初始化例程)只是为了找到少数类。您可能会有不同的看法(当然,这是额外的努力,虽然我不会称其为浪费),但我遇到的主要库的创建者都是这样做的。
【解决方案4】:

任何现代 IDE(eclipse、IntelliJ、...)都应该能够找到特定接口(或类或方法)的用法。

如果您有 jar 文件的源代码,您可以将其作为依赖项添加到您的项目中。或者,您可以简单地解压缩 jar 的内容并在 IDE 中将其作为项目打开,然后找到您感兴趣的接口的用法。

【讨论】:

    【解决方案5】:

    您可以为此使用 IntelliJ。创建一个虚拟项目,并将您的 jars 添加到类路径中。然后打开您的界面,然后单击界面名称左侧的 (I) 图标。不确定 Ecilse,但很确定你也可以这样做。

    课程也是如此。

    【讨论】:

      猜你喜欢
      • 2014-04-05
      • 2023-03-03
      • 2010-09-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多