首页 文章

你能用反射找到包中的所有类吗?

提问于
浏览
454

是否可以找到给定包中的所有类或接口? (快速查看例如Package,似乎没有 . )

23 回答

  • 4

    由于类加载器的动态特性,这是不可能的 . 类加载器不需要告诉VM它可以提供哪些类,而是只需要处理类的请求,并且必须返回类或抛出异常 .

    但是,如果您编写自己的类加载器,或检查类路径及其 jar ,则可以找到此信息 . 这将通过文件系统操作,而不是反射 . 甚至可能有图书馆可以帮助你做到这一点 .

    如果有生成或远程交付的类,您将无法发现这些类 .

    相反,常规方法是在某个地方注册您需要在文件中访问的类,或者在不同的类中引用它们 . 或者只是在命名时使用约定 .

    附录:The Reflections Library将允许您在当前类路径中查找类 . 它可用于获取包中的所有类:

    Reflections reflections = new Reflections("my.project.prefix");
    
     Set<Class<? extends Object>> allClasses = 
         reflections.getSubTypesOf(Object.class);
    
  • 3

    你应该看看开源Reflections library . 有了它,您可以轻松实现您想要的 .

    首先,设置反射索引(因为默认情况下禁用搜索所有类,所以它有点乱):

    List<ClassLoader> classLoadersList = new LinkedList<ClassLoader>();
    classLoadersList.add(ClasspathHelper.contextClassLoader());
    classLoadersList.add(ClasspathHelper.staticClassLoader());
    
    Reflections reflections = new Reflections(new ConfigurationBuilder()
        .setScanners(new SubTypesScanner(false /* don't exclude Object.class */), new ResourcesScanner())
        .setUrls(ClasspathHelper.forClassLoader(classLoadersList.toArray(new ClassLoader[0])))
        .filterInputsBy(new FilterBuilder().include(FilterBuilder.prefix("org.your.package"))));
    

    然后,您可以查询给定包中的所有对象:

    Set<Class<?>> classes = reflections.getSubTypesOf(Object.class);
    
  • 0

    Google Guava 14包含一个新类ClassPath,其中包含三种扫描顶级类的方法:

    • getTopLevelClasses()

    • getTopLevelClasses(String packageName)

    • getTopLevelClassesRecursive(String packageName)

    有关详细信息,请参阅ClassPath javadocs .

  • 5

    您可以使用this method 1使用 ClassLoader .

    /**
     * Scans all classes accessible from the context class loader which belong to the given package and subpackages.
     *
     * @param packageName The base package
     * @return The classes
     * @throws ClassNotFoundException
     * @throws IOException
     */
    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()]);
    }
    
    /**
     * Recursive method used to find all classes in a given directory and subdirs.
     *
     * @param directory   The base directory
     * @param packageName The package name for classes found inside the base directory
     * @return The classes
     * @throws ClassNotFoundException
     */
    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;
    }
    

    1此方法最初来自http://snippets.dzone.com/posts/show/4831,该文件由Internet Archive存档,与现在相关联 . 该片段也可在https://dzone.com/articles/get-all-classes-within-package上找到 .

  • 10

    Spring 天

    此示例适用于Spring 4,但您也可以在早期版本中找到类路径扫描程序 .

    // create scanner and disable default filters (that is the 'false' argument)
    final ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);
    // add include filters which matches all the classes (or use your own)
    provider.addIncludeFilter(new RegexPatternTypeFilter(Pattern.compile(".*")));
    
    // get matching classes defined in the package
    final Set<BeanDefinition> classes = provider.findCandidateComponents("my.package.name");
    
    // this is how you can load the class type from BeanDefinition instance
    for (BeanDefinition bean: classes) {
        Class<?> clazz = Class.forName(bean.getBeanClassName());
        // ... do your magic with the class ...
    }
    

    Google Guava

    Note: 在版本14中,API仍标记为@Beta,因此请注意 生产环境 代码 .

    final ClassLoader loader = Thread.currentThread().getContextClassLoader();
    
    for (final ClassPath.ClassInfo info : ClassPath.from(loader).getTopLevelClasses()) {
      if (info.getName().startsWith("my.package.")) {
        final Class<?> clazz = info.load();
        // do something with your clazz
      }
    }
    
  • 1

    你好 . 我总是遇到上述解决方案(以及其他网站)的一些问题 .
    作为开发人员,我正在为API编写插件 . API允许使用任何外部库或第三方工具 . 该设置还包含jar或zip文件中的代码和直接位于某些目录中的类文件的混合 . 所以我的代码必须能够在每个设置下工作 . 经过大量研究后,我提出了一种方法,该方法至少可以在所有可能的设置中使用95% .

    以下代码基本上是总是有效的过度杀伤方法 .

    代码:

    此代码扫描给定包中包含的所有类 . 它只适用于当前 ClassLoader 中的所有类 .

    /**
     * Private helper method
     * 
     * @param directory
     *            The directory to start with
     * @param pckgname
     *            The package name to search for. Will be needed for getting the
     *            Class object.
     * @param classes
     *            if a file isn't loaded but still is in the directory
     * @throws ClassNotFoundException
     */
    private static void checkDirectory(File directory, String pckgname,
            ArrayList<Class<?>> classes) throws ClassNotFoundException {
        File tmpDirectory;
    
        if (directory.exists() && directory.isDirectory()) {
            final String[] files = directory.list();
    
            for (final String file : files) {
                if (file.endsWith(".class")) {
                    try {
                        classes.add(Class.forName(pckgname + '.'
                                + file.substring(0, file.length() - 6)));
                    } catch (final NoClassDefFoundError e) {
                        // do nothing. this class hasn't been found by the
                        // loader, and we don't care.
                    }
                } else if ((tmpDirectory = new File(directory, file))
                        .isDirectory()) {
                    checkDirectory(tmpDirectory, pckgname + "." + file, classes);
                }
            }
        }
    }
    
    /**
     * Private helper method.
     * 
     * @param connection
     *            the connection to the jar
     * @param pckgname
     *            the package name to search for
     * @param classes
     *            the current ArrayList of all classes. This method will simply
     *            add new classes.
     * @throws ClassNotFoundException
     *             if a file isn't loaded but still is in the jar file
     * @throws IOException
     *             if it can't correctly read from the jar file.
     */
    private static void checkJarFile(JarURLConnection connection,
            String pckgname, ArrayList<Class<?>> classes)
            throws ClassNotFoundException, IOException {
        final JarFile jarFile = connection.getJarFile();
        final Enumeration<JarEntry> entries = jarFile.entries();
        String name;
    
        for (JarEntry jarEntry = null; entries.hasMoreElements()
                && ((jarEntry = entries.nextElement()) != null);) {
            name = jarEntry.getName();
    
            if (name.contains(".class")) {
                name = name.substring(0, name.length() - 6).replace('/', '.');
    
                if (name.contains(pckgname)) {
                    classes.add(Class.forName(name));
                }
            }
        }
    }
    
    /**
     * Attempts to list all the classes in the specified package as determined
     * by the context class loader
     * 
     * @param pckgname
     *            the package name to search
     * @return a list of classes that exist within that package
     * @throws ClassNotFoundException
     *             if something went wrong
     */
    public static ArrayList<Class<?>> getClassesForPackage(String pckgname)
            throws ClassNotFoundException {
        final ArrayList<Class<?>> classes = new ArrayList<Class<?>>();
    
        try {
            final ClassLoader cld = Thread.currentThread()
                    .getContextClassLoader();
    
            if (cld == null)
                throw new ClassNotFoundException("Can't get class loader.");
    
            final Enumeration<URL> resources = cld.getResources(pckgname
                    .replace('.', '/'));
            URLConnection connection;
    
            for (URL url = null; resources.hasMoreElements()
                    && ((url = resources.nextElement()) != null);) {
                try {
                    connection = url.openConnection();
    
                    if (connection instanceof JarURLConnection) {
                        checkJarFile((JarURLConnection) connection, pckgname,
                                classes);
                    } else if (connection instanceof FileURLConnection) {
                        try {
                            checkDirectory(
                                    new File(URLDecoder.decode(url.getPath(),
                                            "UTF-8")), pckgname, classes);
                        } catch (final UnsupportedEncodingException ex) {
                            throw new ClassNotFoundException(
                                    pckgname
                                            + " does not appear to be a valid package (Unsupported encoding)",
                                    ex);
                        }
                    } else
                        throw new ClassNotFoundException(pckgname + " ("
                                + url.getPath()
                                + ") does not appear to be a valid package");
                } catch (final IOException ioex) {
                    throw new ClassNotFoundException(
                            "IOException was thrown when trying to get all resources for "
                                    + pckgname, ioex);
                }
            }
        } catch (final NullPointerException ex) {
            throw new ClassNotFoundException(
                    pckgname
                            + " does not appear to be a valid package (Null pointer exception)",
                    ex);
        } catch (final IOException ioex) {
            throw new ClassNotFoundException(
                    "IOException was thrown when trying to get all resources for "
                            + pckgname, ioex);
        }
    
        return classes;
    }
    

    这三种方法使您能够查找给定包中的所有类 .
    你这样使用它:

    getClassesForPackage("package.your.classes.are.in");
    

    解释:

    该方法首先获得当前 ClassLoader . 然后它获取包含所述包的所有资源并迭代这些 URL . 然后它创建一个 URLConnection 并确定我们拥有什么类型的URl . 它可以是目录( FileURLConnection ),也可以是jar或zip文件中的目录( JarURLConnection ) . 根据我们有什么类型的连接,将调用两种不同的方法 .

    首先让我们看看如果是 FileURLConnection 会发生什么 .
    它首先检查传递的文件是否存在并且是否是目录 . 如果是这种情况,它会检查它是否是一个类文件 . 如果是这样,将创建 Class 对象并将其放入 ArrayList . 如果它不是类文件而是目录,我们只需迭代它并执行相同的操作 . 所有其他案例/文件将被忽略 .

    如果 URLConnectionJarURLConnection ,则将调用另一个私有帮助方法 . 此方法迭代zip / jar存档中的所有条目 . 如果一个条目是类文件并且在包内,则将创建 Class 对象并将其存储在 ArrayList 中 .

    解析完所有资源后(main方法)返回 ArrayList containsig给定包中的所有类,当前 ClassLoader 知道这些类 .

    如果进程在任何时候都失败,将抛出 ClassNotFoundException ,其中包含有关确切原因的详细信息 .

  • 0

    不使用任何额外的库:

    package test;
    
    import java.io.DataInputStream;
    import java.io.InputStream;
    import java.net.URL;
    import java.util.ArrayList;
    import java.util.List;
    
    public class Test {
        public static void main(String[] args) throws Exception{
            List<Class> classes = getClasses(Test.class.getClassLoader(),"test");
            for(Class c:classes){
                System.out.println("Class: "+c);
            }
        }
    
        public static List<Class> getClasses(ClassLoader cl,String pack) throws Exception{
    
            String dottedPackage = pack.replaceAll("[/]", ".");
            List<Class> classes = new ArrayList<Class>();
            URL upackage = cl.getResource(pack);
    
            DataInputStream dis = new DataInputStream((InputStream) upackage.getContent());
            String line = null;
            while ((line = dis.readLine()) != null) {
                if(line.endsWith(".class")) {
                   classes.add(Class.forName(dottedPackage+"."+line.substring(0,line.lastIndexOf('.'))));
                }
            }
            return classes;
        }
    }
    
  • 2

    通常,类加载器不允许扫描类路径上的所有类 . 但通常唯一使用的类加载器是UrlClassLoader,我们可以从中检索目录和jar文件列表(请参阅getURLs)并逐个打开它们以列出可用的类 . 这种方法称为类路径扫描,在ScannotationReflections中实现 .

    Reflections reflections = new Reflections("my.package");
    Set<Class<? extends Object>> classes = reflections.getSubTypesOf(Object.class);
    

    另一种方法是使用Java Pluggable Annotation Processing API来编写注释处理器,它将在编译时收集所有带注释的类构建索引文件以供运行时使用 . 此机制在ClassIndex库中实现:

    // package-info.java
    @IndexSubclasses
    package my.package;
    
    // your code
    Iterable<Class> classes = ClassIndex.getPackageClasses("my.package");
    

    请注意,由于Java编译器会自动发现类路径中找到的任何处理器,因此扫描完全自动化无需额外设置 .

  • 91

    我写了FastClasspathScanner来解决这个问题 . 它处理许多不同类型的类路径扫描任务,具有简单的API,可与许多不同的ClassLoader和类路径环境一起使用,经过精心并行化,并针对高速和低内存消耗进行了高度优化 . 它甚至可以生成类图的GraphViz可视化,显示类如何相互连接 .

    对于查找给定包中所有类或接口的原始问题,您可以执行以下操作:

    List<String> classNames = new FastClasspathScanner("com.mypackage").scan()
        .getNamesOfAllClasses();
    

    有许多可能的变体 - 请参阅文档(上面链接)以获取完整信息 .

  • 4

    我就是这样做的 . 我扫描所有子文件夹(子包),我不尝试加载匿名类:

    /**
       * Attempts to list all the classes in the specified package as determined
       * by the context class loader, recursively, avoiding anonymous classes
       * 
       * @param pckgname
       *            the package name to search
       * @return a list of classes that exist within that package
       * @throws ClassNotFoundException
       *             if something went wrong
       */
      private static List<Class> getClassesForPackage(String pckgname) throws ClassNotFoundException {
          // This will hold a list of directories matching the pckgname. There may be more than one if a package is split over multiple jars/paths
          ArrayList<File> directories = new ArrayList<File>();
          String packageToPath = pckgname.replace('.', '/');
          try {
              ClassLoader cld = Thread.currentThread().getContextClassLoader();
              if (cld == null) {
                  throw new ClassNotFoundException("Can't get class loader.");
              }
    
              // Ask for all resources for the packageToPath
              Enumeration<URL> resources = cld.getResources(packageToPath);
              while (resources.hasMoreElements()) {
                  directories.add(new File(URLDecoder.decode(resources.nextElement().getPath(), "UTF-8")));
              }
          } catch (NullPointerException x) {
              throw new ClassNotFoundException(pckgname + " does not appear to be a valid package (Null pointer exception)");
          } catch (UnsupportedEncodingException encex) {
              throw new ClassNotFoundException(pckgname + " does not appear to be a valid package (Unsupported encoding)");
          } catch (IOException ioex) {
              throw new ClassNotFoundException("IOException was thrown when trying to get all resources for " + pckgname);
          }
    
          ArrayList<Class> classes = new ArrayList<Class>();
          // For every directoryFile identified capture all the .class files
          while (!directories.isEmpty()){
              File directoryFile  = directories.remove(0);             
              if (directoryFile.exists()) {
                  // Get the list of the files contained in the package
                  File[] files = directoryFile.listFiles();
    
                  for (File file : files) {
                      // we are only interested in .class files
                      if ((file.getName().endsWith(".class")) && (!file.getName().contains("$"))) {
                          // removes the .class extension
                          int index = directoryFile.getPath().indexOf(packageToPath);
                          String packagePrefix = directoryFile.getPath().substring(index).replace('/', '.');;                          
                        try {                  
                          String className = packagePrefix + '.' + file.getName().substring(0, file.getName().length() - 6);                            
                          classes.add(Class.forName(className));                                
                        } catch (NoClassDefFoundError e)
                        {
                          // do nothing. this class hasn't been found by the loader, and we don't care.
                        }
                      } else if (file.isDirectory()){ // If we got to a subdirectory
                          directories.add(new File(file.getPath()));                          
                      }
                  }
              } else {
                  throw new ClassNotFoundException(pckgname + " (" + directoryFile.getPath() + ") does not appear to be a valid package");
              }
          }
          return classes;
      }
    
  • 30

    我把一个简单的github项目放在一起解决了这个问题:

    https://github.com/ddopson/java-class-enumerator

    它应该适用于BOTH基于文件的类路径和jar文件 .

    如果在签出项目后运行'make',它将打印出来:

    Cleaning...
    rm -rf build/
     Building...
    javac -d build/classes src/pro/ddopson/ClassEnumerator.java src/test/ClassIShouldFindOne.java src/test/ClassIShouldFindTwo.java src/test/subpkg/ClassIShouldFindThree.java src/test/TestClassEnumeration.java
     Making JAR Files...
    jar cf build/ClassEnumerator_test.jar -C build/classes/ . 
    jar cf build/ClassEnumerator.jar -C build/classes/ pro
     Running Filesystem Classpath Test...
    java -classpath build/classes test.TestClassEnumeration
    ClassDiscovery: Package: 'test' becomes Resource: 'file:/Users/Dopson/work/other/java-class-enumeration/build/classes/test'
    ClassDiscovery: Reading Directory '/Users/Dopson/work/other/java-class-enumeration/build/classes/test'
    ClassDiscovery: FileName 'ClassIShouldFindOne.class'  =>  class 'test.ClassIShouldFindOne'
    ClassDiscovery: FileName 'ClassIShouldFindTwo.class'  =>  class 'test.ClassIShouldFindTwo'
    ClassDiscovery: FileName 'subpkg'  =>  class 'null'
    ClassDiscovery: Reading Directory '/Users/Dopson/work/other/java-class-enumeration/build/classes/test/subpkg'
    ClassDiscovery: FileName 'ClassIShouldFindThree.class'  =>  class 'test.subpkg.ClassIShouldFindThree'
    ClassDiscovery: FileName 'TestClassEnumeration.class'  =>  class 'test.TestClassEnumeration'
     Running JAR Classpath Test...
    java -classpath build/ClassEnumerator_test.jar  test.TestClassEnumeration
    ClassDiscovery: Package: 'test' becomes Resource: 'jar:file:/Users/Dopson/work/other/java-class-enumeration/build/ClassEnumerator_test.jar!/test'
    ClassDiscovery: Reading JAR file: '/Users/Dopson/work/other/java-class-enumeration/build/ClassEnumerator_test.jar'
    ClassDiscovery: JarEntry 'META-INF/'  =>  class 'null'
    ClassDiscovery: JarEntry 'META-INF/MANIFEST.MF'  =>  class 'null'
    ClassDiscovery: JarEntry 'pro/'  =>  class 'null'
    ClassDiscovery: JarEntry 'pro/ddopson/'  =>  class 'null'
    ClassDiscovery: JarEntry 'pro/ddopson/ClassEnumerator.class'  =>  class 'null'
    ClassDiscovery: JarEntry 'test/'  =>  class 'null'
    ClassDiscovery: JarEntry 'test/ClassIShouldFindOne.class'  =>  class 'test.ClassIShouldFindOne'
    ClassDiscovery: JarEntry 'test/ClassIShouldFindTwo.class'  =>  class 'test.ClassIShouldFindTwo'
    ClassDiscovery: JarEntry 'test/subpkg/'  =>  class 'null'
    ClassDiscovery: JarEntry 'test/subpkg/ClassIShouldFindThree.class'  =>  class 'test.subpkg.ClassIShouldFindThree'
    ClassDiscovery: JarEntry 'test/TestClassEnumeration.class'  =>  class 'test.TestClassEnumeration'
     Tests Passed.
    

    另见my other answer

  • 12

    是的,你可以使用很少的API,这就是我喜欢这样做的方式,面对这个我使用hibernate核心的问题并且必须找到带有某个注释注释的类 .

    将这些作为自定义注释,使用该注释标记要拾取的类 .

    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    public @interface EntityToBeScanned {
    
    }
    

    然后用它来标记你的课程

    @EntityToBeScanned 
    public MyClass{
    
    }
    

    使此实用程序类具有以下方法

    public class ClassScanner {
    
        public static Set<Class<?>> allFoundClassesAnnotatedWithEntityToBeScanned(){
            Reflections reflections = new Reflections(".*");
            Set<Class<?>> annotated = reflections.getTypesAnnotatedWith(EntityToBeScanned.class);
            return annotated;
        }
    
    }
    

    调用 allFoundClassesAnnotatedWithEntityToBeScanned() 方法以获取找到的 Set 类 .

    您将需要下面给出的库

    <!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>21.0</version>
        </dependency>
    <!-- https://mvnrepository.com/artifact/org.javassist/javassist -->
    <dependency>
        <groupId>org.javassist</groupId>
        <artifactId>javassist</artifactId>
        <version>3.22.0-CR1</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.reflections/reflections -->
    <dependency>
        <groupId>org.reflections</groupId>
        <artifactId>reflections</artifactId>
        <version>0.9.10</version>
    </dependency>
    
  • 80

    您需要在类路径中查找每个类加载器条目:

    String pkg = "org/apache/commons/lang";
        ClassLoader cl = ClassLoader.getSystemClassLoader();
        URL[] urls = ((URLClassLoader) cl).getURLs();
        for (URL url : urls) {
            System.out.println(url.getFile());
            File jar = new File(url.getFile());
            // ....
        }
    

    如果entry是目录,只需在右侧子目录中查找:

    if (jar.isDirectory()) {
        File subdir = new File(jar, pkg);
        if (!subdir.exists())
            continue;
        File[] files = subdir.listFiles();
        for (File file : files) {
            if (!file.isFile())
                continue;
            if (file.getName().endsWith(".class"))
                System.out.println("Found class: "
                        + file.getName().substring(0,
                                file.getName().length() - 6));
        }
    }
    

    如果条目是文件,并且它是jar,请检查它的ZIP条目:

    else {
        // try to open as ZIP
        try {
            ZipFile zip = new ZipFile(jar);
            for (Enumeration<? extends ZipEntry> entries = zip
                    .entries(); entries.hasMoreElements();) {
                ZipEntry entry = entries.nextElement();
                String name = entry.getName();
                if (!name.startsWith(pkg))
                    continue;
                name = name.substring(pkg.length() + 1);
                if (name.indexOf('/') < 0 && name.endsWith(".class"))
                    System.out.println("Found class: "
                            + name.substring(0, name.length() - 6));
            }
        } catch (ZipException e) {
            System.out.println("Not a ZIP: " + e.getMessage());
        } catch (IOException e) {
            System.err.println(e.getMessage());
        }
    }
    

    现在,一旦你有了包的所有类名,你可以尝试用反射加载它们并分析它们是类还是接口等 .

  • 165

    我一直在尝试使用Reflections库,但是使用它时遇到了一些问题,而且我应该包含太多的jar只是为了简单地获取包上的类 .

    我在这个重复的问题中找到了'll post a solution I':How to get all classes names in a package?

    answer was written by sp00m;我添加了一些更正以使其工作:

    import java.io.File;
    import java.io.IOException;
    import java.net.URL;
    import java.util.Enumeration;
    import java.util.LinkedList;
    import java.util.List;
    
    public final class ClassFinder {
    
        private final static char DOT = '.';
        private final static char SLASH = '/';
        private final static String CLASS_SUFFIX = ".class";
        private final static String BAD_PACKAGE_ERROR = "Unable to get resources from path '%s'. Are you sure the given '%s' package exists?";
    
        public final static List<Class<?>> find(final String scannedPackage) {
            final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            final String scannedPath = scannedPackage.replace(DOT, SLASH);
            final Enumeration<URL> resources;
            try {
                resources = classLoader.getResources(scannedPath);
            } catch (IOException e) {
                throw new IllegalArgumentException(String.format(BAD_PACKAGE_ERROR, scannedPath, scannedPackage), e);
            }
            final List<Class<?>> classes = new LinkedList<Class<?>>();
            while (resources.hasMoreElements()) {
                final File file = new File(resources.nextElement().getFile());
                classes.addAll(find(file, scannedPackage));
            }
            return classes;
        }
    
        private final static List<Class<?>> find(final File file, final String scannedPackage) {
            final List<Class<?>> classes = new LinkedList<Class<?>>();
            if (file.isDirectory()) {
                for (File nestedFile : file.listFiles()) {
                    classes.addAll(find(nestedFile, scannedPackage));
                }
            //File names with the $1, $2 holds the anonymous inner classes, we are not interested on them. 
            } else if (file.getName().endsWith(CLASS_SUFFIX) && !file.getName().contains("$")) {
    
                final int beginIndex = 0;
                final int endIndex = file.getName().length() - CLASS_SUFFIX.length();
                final String className = file.getName().substring(beginIndex, endIndex);
                try {
                    final String resource = scannedPackage + DOT + className;
                    classes.add(Class.forName(resource));
                } catch (ClassNotFoundException ignore) {
                }
            }
            return classes;
        }
    
    }
    

    要使用它,只需将find方法作为本示例中提到的sp00n调用:如果需要,我已添加了类的实例的创建 .

    List<Class<?>> classes = ClassFinder.find("com.package");
    
    ExcelReporting excelReporting;
    for (Class<?> aClass : classes) {
        Constructor constructor = aClass.getConstructor();
        //Create an object of the class type
        constructor.newInstance();
        //...
    }
    
  • -3

    几乎所有答案都使用 Reflections 或从文件系统中读取类文件 . 如果您尝试从文件系统中读取类,则在将应用程序打包为JAR或其他时可能会出错 . 此外,您可能不希望为此目的使用单独的库 .

    这是另一种方法,它是纯java而不依赖于文件系统 .

    import javax.tools.JavaFileObject;
    import javax.tools.StandardJavaFileManager;
    import javax.tools.StandardLocation;
    import javax.tools.ToolProvider;
    import java.io.File;
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.Collections;
    import java.util.regex.Pattern;
    import java.util.stream.Collectors;
    import java.util.stream.StreamSupport;
    
    public class PackageUtil {
    
        public static Collection<Class> getClasses(final String pack) throws Exception {
            final StandardJavaFileManager fileManager = ToolProvider.getSystemJavaCompiler().getStandardFileManager(null, null, null);
            return StreamSupport.stream(fileManager.list(StandardLocation.CLASS_PATH, pack, Collections.singleton(JavaFileObject.Kind.CLASS), false).spliterator(), false)
                    .map(javaFileObject -> {
                        try {
                            final String[] split = javaFileObject.getName()
                                    .replace(".class", "")
                                    .replace(")", "")
                                    .split(Pattern.quote(File.separator));
    
                            final String fullClassName = pack + "." + split[split.length - 1];
                            return Class.forName(fullClassName);
                        } catch (ClassNotFoundException e) {
                            throw new RuntimeException(e);
                        }
    
                    })
                    .collect(Collectors.toCollection(ArrayList::new));
        }
    }
    

    Java 8 is not a must . 您可以使用for循环而不是stream . 你可以像这样测试它

    public static void main(String[] args) throws Exception {
        final String pack = "java.nio.file"; // Or any other package
        PackageUtil.getClasses(pack).stream().forEach(System.out::println);
    }
    
  • 106

    值得一提

    如果你想在某个包下有一个所有类的列表,你可以使用 Reflection 以下方式:

    List<Class> myTypes = new ArrayList<>();
    
    Reflections reflections = new Reflections("com.package");
    for (String s : reflections.getStore().get(SubTypesScanner.class).values()) {
        myTypes.add(Class.forName(s));
    }
    

    这将创建一个类列表,以后您可以根据需要使用它们 .

  • 2

    Aleksander Blomskøld's solution在使用Maven时对我的参数化测试 @RunWith(Parameterized.class) 不起作用 . 测试被正确命名,也找到了但未执行的地方:

    -------------------------------------------------------
    T E S T S
    -------------------------------------------------------
    Running some.properly.named.test.run.with.maven.SomeTest
    Tests run: 0, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.123 sec
    

    据报道,类似的问题是here .

    在我的情况下, @Parameters 正在创建包中每个类的实例 . 在IDE中本地运行时,测试运行良好 . 但是,在运行Maven时,没有使用AleksanderBlomskøld的解决方案找到的类 .

    我确实使用了以下剪辑,其灵感来自DavidPärsson对AleksanderBlomskøld的答案的评论:

    Reflections reflections = new Reflections(new ConfigurationBuilder()
                .setScanners(new SubTypesScanner(false /* don't exclude Object.class */), new ResourcesScanner())
                .addUrls(ClasspathHelper.forJavaClassPath()) 
                .filterInputsBy(new FilterBuilder()
                .include(FilterBuilder.prefix(basePackage))));
    
    Set<Class<?>> subTypesOf = reflections.getSubTypesOf(Object.class);
    
  • 6

    如果您没有使用任何动态类加载器,则可以搜索类路径,并为每个条目搜索目录或JAR文件 .

  • 3

    我刚写了一个util类,它包含测试方法,你可以检查一下〜

    IteratePackageUtil.java:

    package eric.j2se.reflect;
    
    import java.util.Set;
    
    import org.reflections.Reflections;
    import org.reflections.scanners.ResourcesScanner;
    import org.reflections.scanners.SubTypesScanner;
    import org.reflections.util.ClasspathHelper;
    import org.reflections.util.ConfigurationBuilder;
    import org.reflections.util.FilterBuilder;
    
    /**
     * an util to iterate class in a package,
     * 
     * @author eric
     * @date Dec 10, 2013 12:36:46 AM
     */
    public class IteratePackageUtil {
        /**
         * <p>
         * Get set of all class in a specified package recursively. this only support lib
         * </p>
         * <p>
         * class of sub package will be included, inner class will be included,
         * </p>
         * <p>
         * could load class that use the same classloader of current class, can't load system packages,
         * </p>
         * 
         * @param pkg
         *            path of a package
         * @return
         */
        public static Set<Class<? extends Object>> getClazzSet(String pkg) {
            // prepare reflection, include direct subclass of Object.class
            Reflections reflections = new Reflections(new ConfigurationBuilder().setScanners(new SubTypesScanner(false), new ResourcesScanner())
                    .setUrls(ClasspathHelper.forClassLoader(ClasspathHelper.classLoaders(new ClassLoader[0])))
                    .filterInputsBy(new FilterBuilder().includePackage(pkg)));
    
            return reflections.getSubTypesOf(Object.class);
        }
    
        public static void test() {
            String pkg = "org.apache.tomcat.util";
    
            Set<Class<? extends Object>> clazzSet = getClazzSet(pkg);
            for (Class<? extends Object> clazz : clazzSet) {
                System.out.println(clazz.getName());
            }
        }
    
        public static void main(String[] args) {
            test();
        }
    }
    
  • 2

    这是非常可能的,但没有像 Reflections 这样的额外库,很难......
    这很难,因为你没有完整的仪器来获取类名 .
    而且,我接受了 ClassFinder 类的代码:

    package play.util;
    
    import java.io.File;
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.Enumeration;
    import java.util.List;
    import java.util.jar.JarEntry;
    import java.util.jar.JarFile;
    
    /**
     * Created by LINKOR on 26.05.2017 in 15:12.
     * Date: 2017.05.26
     */
    public class FileClassFinder {
    private JarFile file;
    private boolean trouble;
    public FileClassFinder(String filePath) {
        try {
            file = new JarFile(filePath);
        } catch (IOException e) {
            trouble = true;
        }
    }
    
    public List<String> findClasses(String pkg) {
        ArrayList<String> classes = new ArrayList<>();
        Enumeration<JarEntry> entries = file.entries();
        while (entries.hasMoreElements()) {
            JarEntry cls = entries.nextElement();
            if (!cls.isDirectory()) {
                String fileName = cls.getName();
                String className = fileName.replaceAll("/",         ".").replaceAll(File.pathSeparator, ".").substring(0, fileName.lastIndexOf('.'));
                if (className.startsWith(pkg)) classes.add(className.substring(pkg.length() + 1));
            }
        }
        return classes;
    }
    }
    
  • 1

    基于@Staale's answer,并试图不依赖第三方库,我将通过检查第一个包物理位置来实现文件系统方法:

    import java.io.File;
    import java.io.FileFilter;
    import java.util.ArrayList;
    ...
    Class<?>[] foundClasses = new Class<?>[0];
    final ArrayList<Class<?>> foundClassesDyn = new ArrayList<Class<?>>();
    
    new java.io.File(
        klass.getResource(
            "/" + curPackage.replace( "." , "/")
        ).getFile()
    ).listFiles(
        new java.io.FileFilter() {
            public boolean accept(java.io.File file) {
                final String classExtension = ".class";
    
                if ( file.isFile()
                    && file.getName().endsWith(classExtension)
                    // avoid inner classes
                    && ! file.getName().contains("$") )
                {
                    try {
                        String className = file.getName();
                        className = className.substring(0, className.length() - classExtension.length());
                        foundClassesDyn.add( Class.forName( curPackage + "." + className ) );
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace(System.out);
                    }
                }
    
                return false;
            }
        }
    );
    
    foundClasses = foundClassesDyn.toArray(foundClasses);
    
  • 326

    如果您只是想加载一组相关的类,那么Spring可以帮助您 .

    Spring可以在一行代码中实例化实现给定接口的所有类的列表或映射 . 列表或映射将包含实现该接口的所有类的实例 .

    话虽这么说,作为从文件系统中加载类列表的替代方法,而不是实现相同的您要加载的所有类中的接口,无论使用Spring还是使用Spring为您提供所有类的实例 . 这样,您可以加载(并实例化)您想要的所有类,而不管它们是什么包 .

    另一方面,如果将它们全部放在包中是您想要的,那么只需让该包中的所有类实现给定的接口即可 .

  • 1

    这是不可能的,因为包中的所有类可能都没有加载,而你总是知道类的包 .

相关问题