首先,我想说我读过这篇 StackOverflow 帖子
我目前的理解是,如果您想从包(和子包!)中获取所有类而不编写大量代码行,那么您基本上只能使用
Reflections
API。以下是一些选项:
private static Set<Class<?>> getClassesFromPackage(String packageName) {
Reflections reflections = new Reflections(packageName, new SubTypesScanner(false));
return new HashSet<>(reflections.getSubTypesOf(Object.class));
}
并非最佳:
SubTypesScanner
已弃用,因此您会收到警告
private static Set<Class<?>> getClassesFromPackage(String packageName) {
Reflections reflections = new Reflections(packageName, Scanners.SubTypes.filterResultsBy(s -> true));
return new HashSet<>(reflections.getSubTypesOf(Object.class));
}
不是最佳的:
Scanners.SubTypes.filterResultsBy(s -> true)
是笨拙的AF,所以你要么得到不易阅读的代码,要么你必须提供一些元注释
private static Set<Class<?>> getClassesFromPackage(String packageName) {
Reflections reflections = new Reflections(packageName, getStuffThatMakesItWork());
return new HashSet<>(reflections.getSubTypesOf(Object.class));
}
private static Scanners getStuffThatMakesItWork() {
return Scanners.SubTypes.filterResultsBy(s -> true);
}
如果我能写出这个岂不是很美好
private static Set<Class<?>> getClassesFromPackage(String packageName) {
Reflections reflections = new Reflections(packageName);
return new HashSet<>(reflections.getSubTypesOf(Object.class));
}
它的可读性要高得多,但遗憾的是它不起作用(返回的集合将包含零个项目)
扫描包类的最佳方法是什么?
标准:
您的解决方案本质上应该实现此接口(您可能不会在答案中明确执行):
public interface PackageScanner {
Set<Class<?>> getClassesFromPackage(String packageName);
}
我还没有提到的一件事是,自 2021 年以来,整个
Reflections
库就不再被维护,因此依赖它可能不是一个可靠的长期解决方案。有没有至少同样好的替代方案?
UPD:我尝试实现Reilas的建议来使用Class.forName()
,但它导致了一个极其尴尬的代码
// here I reveal that all along I actually wanted to scan for all entities and DTOs
private static void scanForEntitiesAndDtos() {
File sourceRoot = new File("");
entitiesAndDtos.addAll(
Arrays.stream(Objects.requireNonNull(sourceRoot.listFiles()))
.map(clazz -> {
try {
return Class.forName(clazz.getPath());
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
})
.filter(BasicDataGeneratorTest::isEntityOrDto)
.collect(Collectors.toSet())
);
}
private static boolean isEntityOrDto(Class<?> clazz) {
return isEntity(clazz) || isDto(clazz);
}
private static boolean isEntity(Class<?> clazz) {
return clazz.getAnnotation(Entity.class) != null;
}
private static boolean isDto(Class<?> clazz) {
return clazz.getSimpleName().endsWith("Dto");
}
此外,它不起作用,当涉及到Objects.requireNotNull()
时我得到了NPE。我尝试过的事情:
new File("")
new File("com.example.projectname")
new File("com/example/projectname")
new File("src.main.java.com.example.projectname")
new File("src/main/java/com/example/projectname")
listFiles()
不扫描子目录(谁知道,该方法的文档对此保持沉默),但是如果您从
File
返回的字符串创建
file.list()
,则通过
File::isFile
过滤它们,然后使用
Class.forName(file.getPath())
将它们转换为类,它变得更加笨拙(假设它都能工作)
一个答案,其中提到了ClassGraph,一个类似于 Reflections 的库,但很好。
try (ScanResult scanResults = new ClassGraph().acceptPackages(packageName)
.enableClassInfo().scan()) {
Set<Class<?>> entitiesAndDtos = scanResults
.getAllClasses().stream()
.filter(BasicDataGeneratorTest::isEntityOrDto)
.map(ClassInfo::loadClass)
.collect(Collectors.toSet();
}
Jandex也是一个不错的选择。