【问题标题】:Get all of the Classes in the Classpath获取类路径中的所有类
【发布时间】:2011-03-14 10:51:02
【问题描述】:

如何在运行时获取CLASSPATH 中所有可用类的列表?
在 Eclipse IDE 中,您可以通过按 Ctrl+Shift+T 来执行此操作。
Java中有什么方法可以完成吗?

【问题讨论】:

    标签: java reflection class


    【解决方案1】:

    您可以通过将空的String 传递给ClassLoader#getResources() 来获取所有类路径根。

    Enumeration<URL> roots = classLoader.getResources("");
    

    您可以根据URL 构造一个File,如下所示:

    File root = new File(url.getPath());
    

    您可以使用File#listFiles() 获取给定目录中所有文件的列表:

    for (File file : root.listFiles()) {
        // ...
    }
    

    您可以使用标准的java.io.File 方法来检查它是否是目录和/或获取文件名。

    if (file.isDirectory()) {
        // Loop through its listFiles() recursively.
    } else {
        String name = file.getName();
        // Check if it's a .class file or a .jar file and handle accordingly.
    }
    

    根据唯一的功能要求,我猜Reflections library 更符合您的要求。

    【讨论】:

    • “类路径根”是什么意思?我注意到classLoader.getResources("") 只返回类路径上的一两个目录,那些似乎包含我自己编写的 .class 文件的目录(而不是第 3 方包中的那些)。
    • 如果我们想要完整的类路径列表,我们不应该使用URL[] urls = ((URLClassLoader) classLoader).getURLs()吗?
    【解决方案2】:

    这是我写的。我敢肯定,如果你对类路径做任何奇怪的事情,它不会得到一切,但它似乎对我很有效。请注意,它实际上并没有加载类,它只是返回它们的名称。这样它就不会将所有类加载到内存中,并且因为我公司代码库中的某些类如果在错误的时间加载会导致初始化错误......

    public interface Visitor<T> {
        /**
         * @return {@code true} if the algorithm should visit more results,
         * {@code false} if it should terminate now.
         */
        public boolean visit(T t);
    }
    
    public class ClassFinder {
        public static void findClasses(Visitor<String> visitor) {
            String classpath = System.getProperty("java.class.path");
            String[] paths = classpath.split(System.getProperty("path.separator"));
    
            String javaHome = System.getProperty("java.home");
            File file = new File(javaHome + File.separator + "lib");
            if (file.exists()) {
                findClasses(file, file, true, visitor);
            }
    
            for (String path : paths) {
                file = new File(path);
                if (file.exists()) {
                    findClasses(file, file, false, visitor);
                }
            }
        }
    
        private static boolean findClasses(File root, File file, boolean includeJars, Visitor<String> visitor) {
            if (file.isDirectory()) {
                for (File child : file.listFiles()) {
                    if (!findClasses(root, child, includeJars, visitor)) {
                        return false;
                    }
                }
            } else {
                if (file.getName().toLowerCase().endsWith(".jar") && includeJars) {
                    JarFile jar = null;
                    try {
                        jar = new JarFile(file);
                    } catch (Exception ex) {
    
                    }
                    if (jar != null) {
                        Enumeration<JarEntry> entries = jar.entries();
                        while (entries.hasMoreElements()) {
                            JarEntry entry = entries.nextElement();
                            String name = entry.getName();
                            int extIndex = name.lastIndexOf(".class");
                            if (extIndex > 0) {
                                if (!visitor.visit(name.substring(0, extIndex).replace("/", "."))) {
                                    return false;
                                }
                            }
                        }
                    }
                }
                else if (file.getName().toLowerCase().endsWith(".class")) {
                    if (!visitor.visit(createClassName(root, file))) {
                        return false;
                    }
                }
            }
    
            return true;
        }
    
        private static String createClassName(File root, File file) {
            StringBuffer sb = new StringBuffer();
            String fileName = file.getName();
            sb.append(fileName.substring(0, fileName.lastIndexOf(".class")));
            file = file.getParentFile();
            while (file != null && !file.equals(root)) {
                sb.insert(0, '.').insert(0, file.getName());
                file = file.getParentFile();
            }
            return sb.toString();
        }
    }
    

    使用它:

    ClassFinder.findClasses(new Visitor<String>() {
        @Override
        public boolean visit(String clazz) {
            System.out.println(clazz)
            return true; // return false if you don't want to see any more classes
        }
    });
    

    【讨论】:

    • 你应该使用 String[] paths = classpath.split( System.getProperty("path.separator") );否则它将无法在 Linux 上运行
    • 我想试试这个。你能展示一下使用 ClassFinder 的代码吗?
    • 这太可怕了。访问者接口的实现在哪里?
    • @belgariontheking 调用findClasses 的代码应该创建一个实现(通常是一个匿名类)。例如,它与 JavaScript 中传递给 forEach 的回调非常相似。我本可以编写返回 Iterable 的方法,但它会更复杂(如果只有 Java 像许多其他现代语言一样具有生成器函数,让它返回 Iterable 会很简单)。跨度>
    • File.separator 会不会更好,所以代码是可移植的?
    【解决方案3】:

    您可以使用来自 Guava 库的 com.google.common.reflect 包中的实用程序类。例如。获取特定包中的所有类:

        ClassLoader cl = getClass().getClassLoader();
        Set<ClassPath.ClassInfo> classesInPackage = ClassPath.from(cl).getTopLevelClassesRecursive("com.mycompany.mypackage");
    

    这很简洁,但是与其他答案描述的相同的警告仍然适用,即它通常只会找到由“标准”类加载器加载的类,例如URLClassLoader.

    【讨论】:

      【解决方案4】:

      我经常寻找这个。这有点困难,因为即使您设法在类路径上找到所有内容,您也可能找不到给定类加载器可用的所有内容(例如,我曾参与过一个直接从数据库加载类定义的项目)。

      此时最好的选择可能是研究 Spring。他们扫描类路径上的类,看看是否有任何注释,他们需要启动东西。

      这里接受的答案是一个很好的起点:

      Scanning Java annotations at runtime

      【讨论】:

        猜你喜欢
        • 2016-02-20
        • 2011-03-27
        • 2010-11-30
        • 1970-01-01
        • 2021-12-30
        • 2014-10-16
        • 1970-01-01
        • 1970-01-01
        • 2010-12-02
        相关资源
        最近更新 更多