未使用的 Bean 及其移除原因
Quarkus 是一个面向构建时的堆栈,也就是说,它试图在构建时尽可能多地完成工作,以提高应用程序的启动时间和内存使用量。然而,这并不总是像许多现有框架和库那样简单明了,因为它们没有考虑到这种设计选择。CDI 就是其中之一。
为什么要关心
传统的 CDI 容器会尝试在应用程序引导期间查找所有 Bean。为每个 Bean 创建一个元数据对象,并保留到应用程序的整个生命周期。这在运行时具有一定的动态性,但在内存消耗和应用程序启动时间方面是次优的。另一方面,Quarkus 默认会在构建期间尝试检测和删除所有*未使用*的 Bean、拦截器和装饰器。原因很简单。此优化有助于最小化生成的类数量和运行时使用的元数据对象数量,从而节省内存。
说到生成的类。Bean 的发现、验证和所有组件的连接 - 所有这些都是在构建时执行的。Quarkus 然后将收集到的元数据存储在字节码中,也就是说,为每个 Bean 生成一个或多个类。为了满足一些基本的 CDI API 要求,一个 Bean 至少需要一个对应的 javax.enterprise.inject.spi.Bean
实现。如果它是一个正常作用域的 Bean,那么还必须生成一个客户端代理类。最后,被拦截和装饰的 Bean 需要一些更内部的构造。
想象一下,您的应用程序包含 50 个实际上未在任何地方使用的 Bean。如果它们具有正常作用域(例如 @ApplicationScoped
)并且被拦截(例如,声明一个带有 @Transactional
注释的方法),您可以期望生成超过 150 个类。而这些类完全没有用。尽管如此,容器仍然必须实例化并持有对这 50 多个 Bean 元数据类的引用。不用说,当生成原生映像时,Bean 类和任何引用的类都不能被消除死代码。因此,Quarkus 实现了一种算法来摆脱所有这些类。
实际移除了什么?
Quarkus 首先识别所谓的*不可移除* Bean,它们构成了依赖关系树中的根。不可移除的 Bean
-
声明一个观察方法,或
-
通过
@Named
指定名称,或 -
被
@io.quarkus.arc.Unremovable
注释,或 -
通过
quarkus.arc.unremovable-types
配置属性排除,或 -
由 Quarkus 扩展其他方式识别。
最后一点可能最重要,因为这是应用程序入口点被标记为不可移除的方式。一个很好的例子是 JAX-RS 资源类,由 RESTEasy 扩展识别。另一个例子是声明了 @Scheduled
方法的 Bean,由 Scheduler 扩展识别。
一个*未使用*的 Bean
-
不是不可移除的,并且
-
在依赖关系树中不符合注入任何注入点的条件,并且
-
不声明任何符合注入任何注入点条件的生产者,并且
-
不符合注入任何
javax.enterprise.inject.Instance
或javax.inject.Provider
注入点的条件。
最后,未使用的拦截器和装饰器不与任何 Bean 相关联。
在使用开发模式时(例如运行 ./mvnw quarkus:dev ),您可以在 Dev UI 中看到有关哪些 Bean 被移除的更多信息。 |