是否有可能找到给定包中的所有类或接口?(快速看了一下e.g. Package,似乎没有。)


当前回答

您可能应该看看开源的Reflections库。有了它,你可以很容易地实现你想要的。

首先,设置反射索引(这有点乱,因为搜索所有类默认是禁用的):

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);

其他回答

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

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

也就是说,作为从文件系统中加载类列表的替代方法,只需在您想要加载的所有类中实现相同的接口,而不管包是什么,并使用Spring为您提供所有这些类的实例。这样,您就可以加载(并实例化)您想要的所有类,而不管它们在哪个包中。

另一方面,如果您希望将它们都放在一个包中,那么只需让包中的所有类实现给定的接口。

注意,接口本身不需要声明任何方法——它可以完全为空。

要注入实现给定接口的类列表,请使用以下代码行…

  @Autowired
  private List<ISomeInterface> implementationList;

也可以使用Spring注入类的Map。如果有兴趣,请阅读文档。

最后,我将提供一个比搜索整个文件系统树更优雅的解决方案。

创建一个自定义注释,用于构建应用于它的类的目录——类似于@ClassCatalog。

在包测试中定义要扫描的类

package test;

public class A {
  private class B {}

  enum C {}

  record D() {}
}

对于org.reflections:reflections:0.10.2,它为我工作如下:

使用反射库扫描包测试中的类

@Test
void t() {
  final String packagePath = "test";
  final Reflections reflections =
    new Reflections(packagePath, Scanners.SubTypes.filterResultsBy(v -> true));
  reflections.getAll(Scanners.SubTypes).forEach(System.out::println);
}

输出

java.lang.constant.Constable
java.lang.Enum
java.lang.Comparable
java.lang.Record
java.lang.Object
java.io.Serializable
test.A$C
test.A$D
test.A$B
test.A

对于io.github.classgraph:classgraph:4.8.146,它为我工作如下:


@Test
void t() {
  final String packagePath = "test";
  try (ScanResult scanResult = new ClassGraph()
    .enableClassInfo()
    .ignoreClassVisibility()
    .acceptPackages(packagePath)
    .scan()) {
    
    scanResult.getAllClasses()
      .forEach(v -> {
        System.out.println(v.getName());
      });
  }
}

输出

test.A
test.A$B
test.A$C
test.A$D

如果你在Spring-land,你可以使用PathMatchingResourcePatternResolver;

  PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
  Resource[] resources = resolver.getResources("classpath*:some/package/name/*.class");

    Arrays.asList(resources).forEach(r->{
        ...
    });

目前列出给定包中所有类的最健壮的机制是ClassGraph,因为它处理尽可能广泛的类路径规范机制,包括新的JPMS模块系统。(我是作者。)

List<String> classNames = new ArrayList<>();
try (ScanResult scanResult = new ClassGraph().acceptPackages("my.package")
        .enableClassInfo().scan()) {
    classNames.addAll(scanResult.getAllClasses().getNames());
}

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