【问题标题】:Java : loop on all the classes in the classpath [duplicate]Java:循环类路径中的所有类[重复]
【发布时间】:2010-12-02 15:47:10
【问题描述】:

有没有办法遍历类路径中的所有类?

我想对实现某个接口的某些类进行一些反射检查,但我想完全动态地进行,无需输入要检查的类,只需浏览类路径。

【问题讨论】:

  • 如果您提供更多关于您为什么要这样做的信息 - 就像您所拥有的场景一样,可能以不同的方式实现它,但获得相同的结果。

标签: java reflection classpath


【解决方案1】:

你不能优雅地做到这一点。

基本上可以要求类加载器加载特定的类名,但不能要求加载它可以加载的所有类。 (在通过网络加载类的情况下,这样做可能不可行 - 你不能可靠地要求网络服务器告诉你特定目录下的所有文件。)

如果您的类路径处理文件系统,您可能会痛苦地在扩展目录中找到所有 jar 文件,递归正常的类路径目录,并查看所有明确指定的 jar 文件 - 但它会很棘手,也可能很脆弱。

【讨论】:

    【解决方案2】:

    你不能没有自定义类加载器。

    考虑一个例子,你从一个甚至不提供目录列表的远程服务器加载你的类。你怎么可能迭代那里的所有类?

    类加载器接口不提供这样的功能。但如果您使用自己的类加载器(或扩展现有的类加载器),您可以添加此功能。 无论如何,它并不漂亮。

    【讨论】:

      【解决方案3】:

      我认为您必须手动检查它:

      String classpath = System.getProperty("java.class.path");
      String[] locations = classpath.split(System.getProperty("path.separator"));
      // inspect all jar's and class files in these locations, which is a pain in the b%tt
      

      或者使用一些第三方库来做这个(我不知道)。

      【讨论】:

      • 甚至可能不会在那个时间点为您提供每个可加载的类,因为您不知道是否存在任何自定义类加载器(除非您专门寻找类加载器,并分析它们以查看它们是否存在)加载新类...但这就像解决停机问题)。
      • Apache VFS 允许您遍历 JAR 文件的内容。
      【解决方案4】:

      我不知道有哪个库可以做到这一点,但有一些开源项目出于自己的目的这样做。例如,Spring 可以遍历类路径中的类以查找具有特定注释的类。你可以看看他们是如何做到的,以获得一些想法。我将从 org.springframework.context.annotation 包中的类开始,例如 ClassPathScanningCandidateComponentProviderCommonAnnotationBeanPostProcessor

      【讨论】:

        【解决方案5】:

        Reflections 库有助于解决这个问题。正如其他人所说,并非在所有类加载情况下都完全有可能,但如果您只有 jar 和文件,这将可靠地完成。

        【讨论】:

        • 真的很不错的发现...不久前我也在寻找同样的东西...希望我刚刚在这里问过:)
        • FastClasspathScanner 也适用于此。
        • @LukeHutchison:如果您要搜索各种问题,可以将 cmets 添加到广告 FastClasspathScanner,最好明确您与项目的联系。
        • @JonSkeet 很公平,我在其他地方做过。我是 FastClasspathScanner 的主要作者。
        【解决方案6】:

        我已经为单个类加载器解决了这个问题。我需要反思来编写代码来检查 JUnit 测试并报告被忽略的测试。

               /**
               * 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
               */
              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>();
                  try {
                      ClassLoader cld = Thread.currentThread().getContextClassLoader();
                      if (cld == null) {
                          throw new ClassNotFoundException("Can't get class loader.");
                      }
                      String path = pckgname.replace('.', '/');
                      // Ask for all resources for the path
                      Enumeration<URL> resources = cld.getResources(path);
                      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 directory identified capture all the .class files
                  for (File directory : directories) {
                      if (directory.exists()) {
                          // Get the list of the files contained in the package
                          String[] files = directory.list();
                          for (String file : files) {
                              // we are only interested in .class files
                              if (file.endsWith(".class")) {
                                  // removes the .class extension
                                try
                                {
                                  classes.add(Class.forName(pckgname + '.' + file.substring(0, file.length() - 6)));                      
                                }
                                catch (NoClassDefFoundError e)
                                {
                                  // do nothing. this class hasn't been found by the loader, and we don't care.
                                }
                              }
                          }
                      } else {
                          throw new ClassNotFoundException(pckgname + " (" + directory.getPath() + ") does not appear to be a valid package");
                      }
                  }
                  return classes;
              }  
        

        我的解决方案基于code in this thread

        【讨论】:

          【解决方案7】:

          如果您在项目中使用 spring-context:这里是一个使用所需注释枚举所有类型的工作示例:

          /**
           * Lists all types in the given package (recursive) which are annotated by the given annotation.
           * <p>
           * All types which match the criteria are returned, no further checks (interface, abstract, embedded, etc.
           * are performed.
           * <p>
           *
           * @param aAnnotation
           *        the desired annotation type
           * @param aRoot
           *        the package name where to start the search.
           * @return a list of found types
           */
          private Collection<? extends Class<?>> findCandidatesByAnnotation( Class<? extends Annotation> aAnnotation,
                                                                             String aRoot )
          {
              List<Class<?>> result = new ArrayList<Class<?>>();
          
              ClassPathScanningCandidateComponentProvider scanner =
                      new ClassPathScanningCandidateComponentProvider( false )
              {
                  @Override
                  protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition)
                  {
                      return true;
                  }
              };
          
              scanner.addIncludeFilter( new AnnotationTypeFilter( aAnnotation ) );
              Set<BeanDefinition> canditates = scanner.findCandidateComponents( aRoot );
              for ( BeanDefinition beanDefinition : canditates )
              {
                  try
                  {
                      String classname = beanDefinition.getBeanClassName();
                      Class<?> clazz = Class.forName( classname );
                      result.add( clazz );
                  }
                  catch ( ClassNotFoundException t )
                  {
                      myLog.error( "Springs type scanner returns a class name whose class cannot be evaluated!!!", t );
                  }
              }
          
              return result;
          }
          

          此代码在 java 8 下针对 spring-context-4.1.1.RELEASE 进行了测试。

          【讨论】:

            猜你喜欢
            • 2018-10-11
            • 2014-02-09
            • 1970-01-01
            • 2016-03-26
            • 2011-03-14
            • 2010-11-30
            • 2012-09-14
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多