Подтвердить что ты не робот

Java: цикл для всех классов в пути к классам

Есть ли способ перебрать все классы в пути к классам?

Я хочу сделать некоторые рефлексивные проверки для некоторых классов, реализующих определенный интерфейс, но я хочу сделать это полностью динамически, без каких-либо входных данных, какие классы проверять, просто просматривая путь к классам.

4b9b3361

Ответ 1

Библиотека Reflections помогает решить эту проблему. Как заявили другие, это не совсем возможно во всех ситуациях загрузки класса, но если все, что у вас есть, это банки и файлы, это сделает это надежно.

Ответ 2

Вы не можете сделать это элегантно.

В принципе загрузчик классов может быть запрошен для загрузки определенного имени класса, но не может быть запрошен для всех классов, которые он может загрузить. (В случае чего-то, загружающего классы через Интернет, это может быть невыполнимо - вы не можете надежно попросить веб-сервер рассказать вам обо всех файлах под определенным каталогом.)

Если ваш путь к классам связан только с файловой системой, вы можете с трудом найти все файлы jar в каталогах расширений, перезапустить обычные каталоги классов и просмотреть все явно определенные файлы jar, но это будет сложно и, вероятно, хрупким.

Ответ 3

Я решил эту проблему для одного загрузчика классов. Мне понадобилось отражение для написания кода для проверки тестов 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;
      }  

Я основал свое решение в коде в этом потоке:

Ответ 4

Думаю, вам придется вручную его проверять:

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

Или используйте стороннюю библиотеку, которая делает это (я не знаю).

Ответ 5

Я не знаю об этой библиотеке, но есть проекты с открытым исходным кодом, которые делают это в своих целях. Например, Spring может перебирать классы в пути к классам, чтобы найти те, у которых есть определенная аннотация. Вы можете посмотреть, как они это делают, чтобы получить некоторые идеи. Я бы начал с классов в пакете org.springframework.context.annotation, например ClassPathScanningCandidateComponentProvider и CommonAnnotationBeanPostProcessor.

Ответ 6

Если вы используете 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;
}

Этот код был протестирован против spring -context-4.1.1.RELEASE под java 8.

Ответ 7

вы не можете без специального загрузчика классов.

подумайте, например, о том, где вы загружаете свои классы с удаленного сервера, который даже не предлагает список каталогов. как вы можете перебирать все классы там?

интерфейс classloader не предоставляет такую ​​функцию. но если вы используете свой собственный загрузчик классов (или расширяете существующий загрузчик классов), вы можете добавить эту функцию. в любом случае, это не очень.