Spring Boot 2.2中的延迟初始化


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

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

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

spring.main.lazy-initialization=true


延迟初始化的好处
延迟初始化可以显着减少启动时间,因为加载的类更少,并且在应用程序启动期间创建的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的Bean只有这样情况才会被创建:第一次请求发送到Spring MVC DispatcherServlet或Spring WebFlux 的DispatcherHandler。

何时启用延迟初始化
正如我们在上面所看到的,延迟初始化可以显着改善启动时间,但也有一些明显的缺点,重要的是要小心启用它。
懒惰初始化的一个领域是非常有益的,在开发过程中几乎没有任何缺点。当您在应用程序上进行迭代时,延迟初始化和DevTools热重启所提供的启动时间缩短可以显着提高您的工作效率。
另一个可以从延迟初始化中受益的领域是应用程序的集成测试。您可能已经在使用Spring Boot的测试片,通过限制在某些类型的测试中初始化的bean数来减少测试执行时间。延迟初始化提供了另一种实现类似最终结果的机制。如果您无法构建应用程序以使其适合测试切片,或者如果没有可用于特定类型测试的切片,则启用延迟初始化会将初始化的bean限制为那些测试所需要的。这将减少测试执行时间,尤其是在开发期间单独运行测试时。

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