Можете ли вы найти все классы в пакете, используя отражение? - PullRequest
481 голосов
/ 06 февраля 2009

Можно ли найти все классы или интерфейсы в данном пакете? (При быстром взгляде, например, Package, кажется, что нет.)

Ответы [ 24 ]

0 голосов
/ 29 августа 2014

На основании ответа @ Staale и в попытке не полагаться на сторонние библиотеки, я бы реализовал подход файловой системы, проверив физическое местоположение первого пакета с помощью:

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);
0 голосов
/ 05 октября 2017

Если вы просто хотите загрузить группу связанных классов, Spring может помочь вам.

Spring может создавать список или карту всех классов, которые реализуют данный интерфейс, в одной строке кода. Список или карта будут содержать экземпляры всех классов, которые реализуют этот интерфейс.

При этом в качестве альтернативы загрузке списка классов из файловой системы вместо этого просто реализуйте один и тот же интерфейс во всех классах, которые вы хотите загрузить, независимо от пакета, и используйте Spring для предоставления вам экземпляров всех их. Таким образом, вы можете загрузить (и создать экземпляр) все нужные вам классы независимо от того, в каком пакете они находятся.

С другой стороны, если вы хотите иметь их все в пакете, просто включите все классы в этом пакете для реализации данного интерфейса.

0 голосов
/ 28 мая 2019

plain java: FindAllClassesUsingPlainJavaReflectionTest.java

@Slf4j
class FindAllClassesUsingPlainJavaReflectionTest {

  private static final Function<Throwable, RuntimeException> asRuntimeException = throwable -> {
    log.error(throwable.getLocalizedMessage());
    return new RuntimeException(throwable);
  };

  private static final Function<String, Collection<Class<?>>> findAllPackageClasses = basePackageName -> {

    Locale locale = Locale.getDefault();
    Charset charset = StandardCharsets.UTF_8;
    val fileManager = ToolProvider.getSystemJavaCompiler()
                                  .getStandardFileManager(/* diagnosticListener */ null, locale, charset);

    StandardLocation location = StandardLocation.CLASS_PATH;
    JavaFileObject.Kind kind = JavaFileObject.Kind.CLASS;
    Set<JavaFileObject.Kind> kinds = Collections.singleton(kind);
    val javaFileObjects = Try.of(() -> fileManager.list(location, basePackageName, kinds, /* recurse */ true))
                             .getOrElseThrow(asRuntimeException);

    String pathToPackageAndClass = basePackageName.replace(".", File.separator);
    Function<String, String> mapToClassName = s -> {
      String prefix = Arrays.stream(s.split(pathToPackageAndClass))
                            .findFirst()
                            .orElse("");
      return s.replaceFirst(prefix, "")
              .replaceAll(File.separator, ".");
    };

    return StreamSupport.stream(javaFileObjects.spliterator(), /* parallel */ true)
                        .filter(javaFileObject -> javaFileObject.getKind().equals(kind))
                        .map(FileObject::getName)
                        .map(fileObjectName -> fileObjectName.replace(".class", ""))
                        .map(mapToClassName)
                        .map(className -> Try.of(() -> Class.forName(className))
                                             .getOrElseThrow(asRuntimeException))
                        .collect(Collectors.toList());
  };

  @Test
  @DisplayName("should get classes recursively in given package")
  void test() {
    Collection<Class<?>> classes = findAllPackageClasses.apply(getClass().getPackage().getName());
    assertThat(classes).hasSizeGreaterThan(4);
    classes.stream().map(String::valueOf).forEach(log::info);
  }
}

PS: чтобы упростить шаблоны для обработки ошибок и т. Д., Я использую здесь vavr и lombok библиотеки

другие реализации можно найти в моем репозитории GitHub daggerok / java-рефлексия-найти-аннотированные классы или методы

0 голосов
/ 06 февраля 2009

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

...