【问题标题】:Reflectively get all packages in a project?反射性地获取项目中的所有包?
【发布时间】:2012-03-08 04:08:30
【问题描述】:

如何反思性地获取项目中的所有包?我从 Package.getPackages() 开始,但这只获得了与当前包关联的所有包。有没有办法做到这一点?

【问题讨论】:

  • 您对项目的定义是什么?
  • @Perception - 顶级包和所有子包。我可能应该将项目改写为本地项目或单数 api。
  • @PhilippWendler,感谢您的评论,它让我找到了解决方案。如果您发布答案,它就是您的。

标签: java unit-testing reflection junit


【解决方案1】:

@PhilippWendler 的评论让我找到了一种完成我需要的方法。我稍微调整了该方法以使其具有递归性。

    /**
     * Recursively fetches a list of all the classes in a given
     * directory (and sub-directories) that have the @UnitTestable
     * annotation.
     * @param packageName The top level package to search.
     * @param loader The class loader to use. May be null; we'll
     * just grab the current threads.
     * @return The list of all @UnitTestable classes.
     */
    public List<Class<?>> getTestableClasses(String packageName, ClassLoader loader) {
        // State what package we are exploring
        System.out.println("Exploring package: " + packageName);
        // Create the list that will hold the testable classes
        List<Class<?>> ret = new ArrayList<Class<?>>();
        // Create the list of immediately accessible directories
        List<File> directories = new ArrayList<File>();
        // If we don't have a class loader, get one.
        if (loader == null)
            loader = Thread.currentThread().getContextClassLoader();
        // Convert the package path to file path
        String path = packageName.replace('.', '/');
        // Try to get all of nested directories.
        try {
            // Get all of the resources for the given path
            Enumeration<URL> res = loader.getResources(path);
            // While we have directories to look at, recursively
            // get all their classes.
            while (res.hasMoreElements()) {
                // Get the file path the the directory
                String dirPath = URLDecoder.decode(res.nextElement()
                        .getPath(), "UTF-8");
                // Make a file handler for easy managing
                File dir = new File(dirPath);
                // Check every file in the directory, if it's a
                // directory, recursively add its viable files
                for (File file : dir.listFiles()) {
                    if (file.isDirectory()) 
                        ret.addAll(getTestableClasses(packageName + '.' + file.getName(), loader));
                }
            }
        } catch (IOException e) {
            // We failed to get any nested directories. State
            // so and continue; this directory may still have
            // some UnitTestable classes.
            System.out.println("Failed to load resources for [" + packageName + ']');
        }
        // We need access to our directory, so we can pull
        // all the classes.
        URL tmp = loader.getResource(path);
        System.out.println(tmp);
        if (tmp == null)
            return ret;
        File currDir = new File(tmp.getPath());
        // Now we iterate through all of the classes we find
        for (String classFile : currDir.list()) {
            // Ensure that we only find class files; can't load gif's!
            if (classFile.endsWith(".class")) {
                // Attempt to load the class or state the issue
                try {
                    // Try loading the class
                    Class<?> add = Class.forName(packageName + '.' +
                            classFile.substring(0, classFile.length() - 6));
                    // If the class has the correct annotation, add it
                    if (add.isAnnotationPresent(UnitTestable.class))
                        ret.add(add);
                    else 
                        System.out.println(add.getName() + " is not a UnitTestable class");
                } catch (NoClassDefFoundError e) {
                    // The class loader could not load the class
                    System.out.println("We have found class [" + classFile + "], and couldn't load it.");
                } catch (ClassNotFoundException e) {
                    // We couldn't even find the damn class
                    System.out.println("We could not find class [" + classFile + ']');
                }
            }
        }
        return ret;
    }

【讨论】:

    【解决方案2】:

    这种方法只打印所有包(至少必须先给出根“packageName”)。

    它是从上面派生的。

    package devTools;
    
    import java.io.File;
    import java.io.IOException;
    import java.net.URL;
    import java.net.URLDecoder;
    import java.util.ArrayList;
    import java.util.Enumeration;
    import java.util.List;
    
    public class DevToolUtil {
    
        /**
         * Method prints all packages (at least a root "packageName" has to be given first).
         *
         * @see http://stackoverflow.com/questions/9316726/reflectively-get-all-packages-in-a-project
         * @since 2016-12-05
         * @param packageName
         * @param loader
         * @return List of classes.
         */
        public List<Class<?>> getTestableClasses(final String packageName, ClassLoader loader) {
            System.out.println("Exploring package: " + packageName);
    
            final List<Class<?>> ret = new ArrayList<Class<?>>();
    
            if (loader == null) {
                loader = Thread.currentThread().getContextClassLoader();
            }
    
            final String path = packageName.replace('.', '/');
    
            try {
                final Enumeration<URL> res = loader.getResources(path);
    
                while (res.hasMoreElements()) {
                    final String dirPath = URLDecoder.decode(res.nextElement().getPath(), "UTF-8");
                    final File dir = new File(dirPath);
    
                    if (dir.listFiles() != null) {
                        for (final File file : dir.listFiles()) {
                            if (file.isDirectory()) {
                                final String packageNameAndFile = packageName + '.' + file.getName();
                                ret.addAll(getTestableClasses(packageNameAndFile, loader));
                            }
                        }
                    }
                }
            } catch (final IOException e) {
                System.out.println("Failed to load resources for [" + packageName + ']');
            }
    
            return ret;
        }
    
        public static void main(final String[] args) {
            new DevToolUtil().getTestableClasses("at", null);
        }
    }
    

    【讨论】:

      【解决方案3】:

      可能是题外话(因为它不完全是在java“反射”方面)...... 但是,以下解决方案如何:

      Java 包可以被视为文件夹(或 Linux\UNIX 上的目录)。 假设您有一个根包并且它的绝对路径已知,您可以使用以下批处理作为基础递归打印所有具有 *.java 或 *.class 文件的子文件夹:

      @echo off
      rem List all the subfolders under current dir
      
      FOR /R "." %%G in (.) DO (
       Pushd %%G
       Echo now in %%G
       Popd )
      Echo "back home" 
      

      您可以将此批处理包装在 java 中,或者如果您在 Linux\Unix 上运行,则在一些 shell 脚本中重写它。

      祝你好运!

      Aviad。

      【讨论】:

      • 这不是Java,即使是,它所做的只是列出当前目录的子目录,这与提出的问题无关。
      • @Cedric Beust,您在哪里发现解决方案必须在 java 中?(该项目在 java 中,但作者没有提到有关解决方案的任何限制)。关于这个批处理确实如此,它主要说明了如何在 java 中涉及太多不可读编码的任务可以在更适合目的的其他“技术”中实现。关于批处理做什么或不做什么,它再次打印一个列表当前目录的子目录,这就是问题所在。我的评论让你觉得“奇怪”吗?(你可以随时投票:)
      • 不,问题询问如何在项目中找到“包”。 “packages”是一个 Java 概念,并且该问题被标记为“java”,因此很明显该人在问一个纯粹的 Java 问题。同样,即使不是这种情况,您的代码也会显示如何查找目录,而不是包。
      • @Cedric Beust,就java项目而言,“java包”可以被视为“目录”。请参考docs.oracle.com/javase/tutorial/java/concepts/package.html。此外,我相信我的解决方案更优雅(比您的链接中的 Java 编码更少不可读)并且更正确,因为您的解决方案检索了运行项目使用的所有类的完全限定类路径(以及第 3 方依赖项)和问题是关于获取项目的所有包。
      【解决方案4】:

      这是可能的,但既棘手又昂贵,因为您需要自己走类路径。下面是 TestNG 是怎么做的,你可以自己提取重要的部分:

      https://github.com/cbeust/testng/blob/master/src/main/java/org/testng/internal/PackageUtils.java

      【讨论】:

      • 简要查看链接 -> 否决在类成员名称中使用 Vector 和“m_”前缀。似乎此代码有点过时
      • 大声笑...回报我告诉你你的其他答案是题外话?优雅。
      • 我很高兴我让你“大声笑”。否决票的(部分)原因在我对您的回答的第一条评论中进行了解释。没有什么私人的,真的,但我相信你的解决方案是不正确的,代码是丑陋的。有时,跳出框框思考并使用最适合目的的技术是件好事。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-27
      • 1970-01-01
      • 2020-08-24
      • 2013-06-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多