【问题标题】:Java Package Introspection [duplicate]Java包自省[重复]
【发布时间】:2010-11-12 11:53:43
【问题描述】:

如何获取包中的所有类?

【问题讨论】:

标签: java class packages introspection


【解决方案1】:

你不能。类可以通过许多不同的类加载器进入,包括远程加载器。

【讨论】:

  • 我的库 FastClasspathScanner 处理绝大多数常见的类加载器和类路径规范机制。
【解决方案2】:

没有全局方法可以做到这一点。话虽如此,如果你知道你的类来自哪里,你可以遍历 jar 文件或文件系统的目录。

【讨论】:

    【解决方案3】:

    有一个来自here 的 sn-p 可以满足您的要求,假设可以在本地找到这些类:

    private static Class[] getClasses(String packageName)
    throws ClassNotFoundException, IOException {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        assert classLoader != null;
        String path = packageName.replace('.', '/');
        Enumeration<URL> resources = classLoader.getResources(path);
        List<File> dirs = new ArrayList<File>();
        while (resources.hasMoreElements()) {
            URL resource = resources.nextElement();
            dirs.add(new File(resource.getFile()));
        }
        ArrayList<Class> classes = new ArrayList<Class>();
        for (File directory : dirs) {
            classes.addAll(findClasses(directory, packageName));
        }
        return classes.toArray(new Class[classes.size()]);
    }
    
    private static List<Class> findClasses(File directory, String packageName) throws ClassNotFoundException {
        List<Class> classes = new ArrayList<Class>();
        if (!directory.exists()) {
            return classes;
        }
        File[] files = directory.listFiles();
        for (File file : files) {
            if (file.isDirectory()) {
                assert !file.getName().contains(".");
                classes.addAll(findClasses(file, packageName + "." + file.getName()));
            } else if (file.getName().endsWith(".class")) {
                classes.add(Class.forName(packageName + '.' + file.getName().substring(0, file.getName().length() - 6)));
            }
        }
        return classes;
    }
    

    【讨论】:

    • 这适用于 jar 类吗?
    • 据我所知,如果您有一个 jar (-1),这将失败,但它是寻找 .class 文件以进行发现 (+1) 的一个不错的例程,因此您收支平衡 :)
    • 上面的代码不仅假设给定包中的类是本地的,而且它们都被同一个类加载器加载并且没有被打包。
    • 对于像 Eclipse 这样的系统来说,这仍然是一个有用的开始,其中一个 jar 被放入一个目录并在下次启动时发现。一个合理的解决方案,但非常有限(并且由于它目前不支持 Jars,因此没有那么有用)
    【解决方案4】:

    Java 没有发现功能。

    大多数能够添加(发现)新类的产品要么有一个描述“程序扩展”的文本文件,要么有一个特定的目录,您可以在其中放置使用@JG 描述的技巧的类或 jar。 (这是 eclipse 所做的,推荐用于用户可以手动添加新模块的任何解决方案)

    【讨论】:

    • 不要忘记插件的 SPI API。这些为插件框架提供了一个相当优雅的解决方案。
    【解决方案5】:

    根据 JG 发布的想法,这是解决 jar 问题的更完整方法。

    /**
     * Scans all classloaders for the current thread for loaded jars, and then scans
     * each jar for the package name in question, listing all classes directly under
     * the package name in question. Assumes directory structure in jar file and class
     * package naming follow java conventions (i.e. com.example.test.MyTest would be in
     * /com/example/test/MyTest.class)
     */
    public Collection<Class> getClassesForPackage(String packageName) throws Exception {
      String packagePath = packageName.replace(".", "/");
      ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
      Set<URL> jarUrls = new HashSet<URL>();
    
      while (classLoader != null) {
        if (classLoader instanceof URLClassLoader)
          for (URL url : ((URLClassLoader) classLoader).getURLs())
            if (url.getFile().endsWith(".jar")  // may want better way to detect jar files
              jarUrls.add(url);
    
        classLoader = classLoader.getParent();
      }
    
      Set<Class> classes = new HashSet<Class>();
    
      for (URL url : jarUrls) {
        JarInputStream stream = new JarInputStream(url.openStream()); // may want better way to open url connections
        JarEntry entry = stream.getNextJarEntry();
    
        while (entry != null) {
          String name = entry.getName();
          int i = name.lastIndexOf("/");
    
          if (i > 0 && name.endsWith(".class") && name.substring(0, i).equals(packagePath)) 
            classes.add(Class.forName(name.substring(0, name.length() - 6).replace("/", ".")));
    
          entry = stream.getNextJarEntry();
        }
    
        stream.close();
      }
    
      return classes;
    }
    

    【讨论】:

    • -1 没有文档,甚至没有提示它的作用,+2 因为它看起来像扫描类路径并包含 jars!所以你净+1 :)
    • (嗯,有一个关于它的作用的提示......我收回它)。
    • 添加了一个 javadoc 标头以获得更好的解释。 :)
    • @toluju 太好了!只需要根据我的需要将.equals(packagePath) 更改为.startsWith(packagePath)。谢谢。
    【解决方案6】:

    以上答案概述了所需的功能。但是,可以在herehere 找到有关该主题的更多详细信息。

    【讨论】:

      猜你喜欢
      • 2012-03-27
      • 1970-01-01
      • 2013-08-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-31
      • 1970-01-01
      • 2011-09-09
      相关资源
      最近更新 更多