Spring Boot 2.2中的延迟初始化


最近公布的Spring Boot 2.2的第一个里程碑,引入了延迟初始化的支持。这篇文章描述了新功能,并解释了如何以及何时启用它。

Lazy延迟懒惰是什么意思?
自从11年前源代码迁移到Git之前,Spring Framework已经支持了懒惰的bean初始化。默认情况下,在刷新应用程序上下文时,将创建上下文中的每个bean并注入其依赖项。而现在,当bean定义被配置为懒惰地初始化时,它将不会被创建,并且在需要之前不会注入其依赖项。

启用延迟初始化
在任何版本的Spring Boot中都可以启用延迟初始化,当然如果你很高兴也可以自己动手并写一个BeanFactoryPostProcessor。Spring Boot 2.2只是变得更容易,引入了新属性spring.main.lazy-initialization,(在SpringApplication和SpringApplicationBuilder都有两个一样的方法),当这个属性设置true为时,应用程序中的bean定义将配置为使用延迟初始化。

延迟初始化的好处
延迟初始化可以显着减少启动时间,因为加载的类更少,并且在应用程序启动期间创建的bean更少。例如,使用Actuator和Spring Security的小型Web应用程序通常在2500ms开始,将在2000ms启动,并启用延迟初始化。根据bean的依赖关系图的结构,确切的改进因应用程序而异。

DevTools怎么样
Spring Boot的DevTools已经显着提升了开发人员的工作效率。每次要尝试更改时,DevTools都不需要重新启动JVM和应用程序,而是在同一个JVM中启用应用程序的热重启。热重启的一个显着优点是它使JIT更有机会优化启动应用程序所涉及的代码。重启几次后,2500ms的原始时间减少了近80%,接近500ms。使用延迟初始化,我们可以做得更好。设置spring.main.lazy-initialization直接在IDE中看到我们的应用程序在400ms内重启。

懒惰初始化的缺点
正如我们在上面所看到的,启用延迟初始化可以极大地减少启动时间。您可能想要一直启用它,或者想知道为什么我们没有决定默认启用它。延迟初始化有一些缺点,这意味着我们认为一旦你认为这样做有意义,最好选择加入。
由于不再加载类,并且在需要之前不再创建bean,因此延迟初始化可能会掩盖以前在启动时已识别的问题。此类问题可能包括没有类发现错误(no class def found errors),内存不足错误以及由于配置错误导致的故障。

在Web应用程序中,延迟初始化可能导致触发Bean初始化的HTTP请求的延迟增加。这通常只是第一个请求,但它可能会对负载平衡和自动扩展产生负面影响。

如果您不确定延迟初始化对您的应用程序有什么影响,或者您想验证另一个框架的行为是否满足您的需求并与其声明匹配,那么使用调试器可能会提供信息。通过在其中一个bean的构造函数中放置一个断点,您可以看到它何时被初始化。例如,在启用了延迟初始化的Spring Boot Web应用程序中,您将看到@Controller在第一次请求Spring MVC DispatcherServlet或Spring WebFlux 之前不会创建bean DispatcherHandler。

何时启用延迟初始化
正如我们在上面所看到的,延迟初始化可以显着改善启动时间,但也有一些明显的缺点,重要的是要小心启用它。

懒惰初始化的一个领域是非常有益的,在开发过程中几乎没有任何缺点。当您在应用程序上进行迭代时,延迟初始化和DevTools热重启所提供的启动时间缩短可以显着提高您的工作效率。

另一个可以从延迟初始化中受益的领域是应用程序的集成测试。您可能已经在使用Spring Boot的测试片,通过限制在某些类型的测试中初始化的bean数来减少测试执行时间。延迟初始化提供了另一种实现类似最终结果的机制。如果您无法构建应用程序以使其适合测试切片,或者如果没有可用于特定类型测试的切片,则启用延迟初始化会将初始化的bean限制为那些测试所需要的。这将减少测试执行时间,尤其是在开发期间单独运行测试时。

最后,您可能需要考虑在生产中启用延迟初始化。如果你这样做,应该小心。对于Web应用程序,容器编排可能会受益于/health能够更快响应的端点,但您还需要了解在向应用程序自己的某个端点发出第一个请求时可能会增加延迟。您还应该注意在禁用延迟初始化的情况下调整应用程序的JVM大小,以避免在使用所有组件后出现任何不必要的内存不足错误。