Can you find all classes in a package using reflection?

前端 未结 27 2855
不知归路
不知归路 2020-11-21 05:24

Is it possible to find all classes or interfaces in a given package? (Quickly looking at e.g. Package, it would seem like no.)

27条回答
  •  耶瑟儿~
    2020-11-21 05:43

    plain java: FindAllClassesUsingPlainJavaReflectionTest.java

    @Slf4j
    class FindAllClassesUsingPlainJavaReflectionTest {
    
      private static final Function asRuntimeException = throwable -> {
        log.error(throwable.getLocalizedMessage());
        return new RuntimeException(throwable);
      };
    
      private static final Function>> 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 kinds = Collections.singleton(kind);
        val javaFileObjects = Try.of(() -> fileManager.list(location, basePackageName, kinds, /* recurse */ true))
                                 .getOrElseThrow(asRuntimeException);
    
        String pathToPackageAndClass = basePackageName.replace(".", File.separator);
        Function 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> classes = findAllPackageClasses.apply(getClass().getPackage().getName());
        assertThat(classes).hasSizeGreaterThan(4);
        classes.stream().map(String::valueOf).forEach(log::info);
      }
    }
    

    PS: to simplify boilerplates for handling errors, etc, I'm using here vavr and lombok libraries

    other implementations could be found in my GitHub daggerok/java-reflection-find-annotated-classes-or-methods repo

提交回复
热议问题