在限制范围内获取所有 类

Get all classes within constrains

我正在尝试制作一个从 SQLite 数据库到我的项目逻辑层的通用实例映射器。 映射器中的每个条目都是一些 class “DAL< 名称 >” 的实例,例如 DALProduct 都在同一个包中,扩展 class DALObject 具有以下结构:

public abstract class DALObject{
  abstract String getCreate();
  ... //other logic
}

使用反射,我可以轻松地在每个 class 上调用 getCreate 方法,并从中获得我需要的东西。 问题是我不知道有多少 child classes DALObject 并且我想要一个在添加新映射器时不需要触及的映射器。

我知道你可以得到所有 child class 的 DALObject 已经加载但是我 运行 这个代码在我的程序的最开始在我加载任何 classes.

之前

有什么想法吗?我有一个相当大的约束案例,所以也许我可以以某种方式将其组合在一起。

这将是何时使用注释的完美实例。您可以在每个 class 的声明之前定义注释,表明 class 有一些关于 'DALObjects' 的元数据。然后你可以使用 Reflections 或一些类似的库来轻松找到所有带有此注释的 Class 对象。

例如;

@Target(value=TYPE)
public @interface DALObject {

}
@DALObject
public class MyDALObject {

}
Reflections reflections = new Reflections(new ConfigurationBuilder()
    .setScanners(new TypeAnnotationsScanner()));

Set<Class<?>> annotatedWithDALObject = reflections.getTypesAnnotatedWith(DALObject.class);

这样的任务只能通过扫描 class 路径上的每个 class 来实现(这取决于您使用的库数量,通常 很多 ) 并查看它是否扩展了 DALObject。

如果您想走那条路,org.reflections 库可以帮助您:

Reflections reflections = new Reflections("my.package",
    Reflector.class.getClassLoader(), new SubTypesScanner(false));
Set<Class<? extends Module>> modules = reflections.getSubTypesOf(DALObject.class);

出于性能原因,我强烈建议在您的应用程序启动期间执行一次此扫描并存储结果。

作为一种替代方法(我在这里没有考虑Class的泛型类型安全,这是要实现的):

public abstract class DALObject {
    public static List<Class> subTypes = new ArrayList<>();

    public DALObject() {
        DALObject.subTypes.add(this.getClass());
    }
}

显然这只有在你在某个地方实例化子classes的对象时才有效,如果你的getCreate首先打算这样做,我的建议显然行不通。

据我所知,Reflections 没有得到很好的支持(多年来只有微小的变化)并且仍然 doesn't work with modules。 相反,我建议使用 ClassGraph,它得到了高度积极的维护,并且可以与模块、奇怪的 class 加载器和其他东西一起使用。

您会发现 DALObject 像这样的实现:

ScanResult scanResults = new ClassGraph()
    .acceptPackages(packages) //skip to scan ALL packages
    .enableAllInfo() //Not necessary but gives you more info to filter on, if needed
    .initializeLoadedClasses()
    .scan();

//You can cache, or serialize scanResults using scanResults.toJSON()

List<Class<?>> impls = scanResults.getAllClasses().stream()
    .filter(impl -> impl.extendsSuperclass(DALObject.getName()))
    // or impl.implementsInterface(DALObject.getName()) if DALObject is an interface
    .flatMap(info -> info.loadClass()) //or load the class yourself if you need to customize the logic
    .collect(Collectors.toList());