领先一步
VMware 提供培训和认证,以加速您的进步。
了解更多最近宣布的 Spring Boot 2.2 首个里程碑版本引入了对延迟初始化的支持。这篇文章介绍了新的功能,并解释了如何以及何时启用它。
Spring Framework 在其源代码迁移到 Git 之前的 11 年前就已支持延迟 Bean 初始化。默认情况下,当应用程序上下文刷新时,上下文中的每个 Bean 都会被创建,并且其依赖项会被注入。相比之下,当 Bean 定义配置为延迟初始化时,它将不会被创建,并且其依赖项也不会被注入,直到它被需要。
如果愿意动手编写 BeanFactoryPostProcessor
,则可以在任何版本的 Spring Boot 中启用延迟初始化。Spring Boot 2.2 通过引入一个新的属性 spring.main.lazy-initialization
使其变得更容易(SpringApplication
和 SpringApplicationBuilder
上也有等效的方法)。当设置为 true
时,应用程序中的 Bean 定义将被配置为使用延迟初始化。
延迟初始化可以显著减少启动时间,因为在应用程序启动期间加载的类更少,创建的 Bean 也更少。例如,一个使用 Actuator 和 Spring Security 的小型 Web 应用程序,通常在 2500 毫秒内启动,启用延迟初始化后将在 2000 毫秒内启动。确切的改进因应用程序而异,具体取决于其 Bean 依赖关系图的结构。
Spring Boot 的 DevTools 已经显著提高了开发人员的生产力。无需每次尝试更改时都重新启动 JVM 和应用程序,DevTools 可以在同一个 JVM 中启用应用程序的热重启。热重启的一个显著优势是,它使 JIT 有更多机会优化启动应用程序所涉及的代码。经过几次重启后,最初的 2500 毫秒时间减少了近 80%,接近 500 毫秒。使用延迟初始化,我们可以做得更好。在 IDE 中直接设置 spring.main.lazy-initialization
会使我们的应用程序在 400 毫秒内重启。
如上所述,启用延迟初始化可以显著减少启动时间。您可能想一直启用它,或者想知道为什么我们没有决定默认启用它。延迟初始化有一些缺点,这意味着我们认为在您确定这样做有意义后选择加入它会更好。
由于类不再加载,并且 Bean 不再在需要时创建,因此延迟初始化可能会掩盖一个以前会在启动时识别的問題。此类问题可能包括找不到类定义错误、内存不足错误以及由于配置错误导致的故障。
在 Web 应用程序中,延迟初始化会导致触发 Bean 初始化的 HTTP 请求延迟增加。这通常只是第一个请求,但可能会对负载平衡和自动扩展产生不利影响。
如果您不确定延迟初始化对应用程序有什么影响,或者您想验证另一个框架的行为是否满足您的需求并与其声明相符,则使用调试器进行检查可能会有帮助。通过在其中一个 Bean 的构造函数中设置断点,您可以查看它何时被初始化。例如,在启用了延迟初始化的 Spring Boot Web 应用程序中,您将看到 @Controller
Bean 只有在第一个请求被发送到 Spring MVC 的 DispatcherServlet
或 Spring WebFlux 的 DispatcherHandler
时才会被创建。
如上所述,延迟初始化可以在启动时间方面提供显著的改进,但它也有一些明显的缺点,因此必须谨慎启用它。
延迟初始化在开发过程中可以带来极大的好处,并且几乎没有缺点。在迭代应用程序时,延迟初始化和 DevTools 的热重启提供的启动时间缩短可以显著提高您的生产力。
另一个可以从延迟初始化中获益的领域是应用程序的集成测试。您可能已经在使用 Spring Boot 的测试切片来缩短测试执行时间,方法是限制在某些类型的测试中初始化的 Bean 的数量。延迟初始化提供了另一种机制来实现类似的结果。如果您无法构建应用程序以使其适合测试切片,或者对于特定类型的测试没有可用的切片,则启用延迟初始化将限制初始化的 Bean 为测试所需的 Bean。这将缩短测试执行时间,尤其是在开发过程中单独运行测试时。
最后,您可能希望考虑在生产环境中启用延迟初始化。如果您这样做,则应谨慎操作。对于 Web 应用程序,容器编排可能会受益于能够更快响应的 /health
端点,但您还需要注意在第一次向应用程序自身端点发出请求时潜在的延迟增加。您还应注意在禁用延迟初始化的情况下调整应用程序的 JVM 大小,以避免所有组件都使用后出现任何意外的内存不足错误。