Springboot2.2版本的延迟初始化
在最近公布的Spring Boot 2.2的第一个里程碑,引入了延迟初始化的支持。这篇文章描述这个新功能,并解释了如何以及何时启用它。
懒惰Lazy是什么意思?
自从11年前源代码迁移到Git之前,Spring Framework已经支持了延迟的bean初始化。默认情况下,在刷新应用程序上下文时,将创建上下文中的每个bean并注入其依赖。相反,如果将bean定义配置为延迟初始化时,它就不会被创建,并且直到需要时才注入其依赖。
启用延迟初始化
如果你乐于动手编写一个BeanFactoryPostProcessor,那么在任何版本的Spring Boot中都可以启用延迟初始化。Spring Boot 2.2只是通过引入这种新的配置spring.main.lazy-initialization
让延迟初始化变得更容易,(在SpringApplication和SpringApplicationBuilder中也有与这个新配置相同的方法),当其设置true为时,bean在应用程序中的定义将被配置为延迟初始化。
spring.main.lazy-initialization=true
延迟初始化的好处
延迟初始化可以显著减少启动时间,因为在应用程序启动期间加载的类和创建的bean更少。例如,一个使用执行器和Spring安全性的小型web应用程序通常在2500毫秒内启动,它将在启用延迟初始化的情况下在2000毫秒内启动。具体的改进将因应用程序的不同而有所不同,这取决于bean依赖关系图的结构。
DevTools怎么样?
Spring Boot的DevTools已经很明显提高了开发人员的工作效率。与每次尝试更改时都必须重启JVM和应用程序不同,DevTools支持在相同的JVM中热重启应用程序。热重启的一个重要好处是,它使JIT有更多的机会优化启动应用程序所涉及的代码。重新启动几次后,原来的2500ms时间减少了近80%,接近500ms。使用延迟初始化,时间可以再次压缩。设置spring.main.lazy-initialization=true
,在IDE中可以看到应用程序重新启动只需要400ms.
延迟初始化的缺点
正如上所述,启用延迟初始化可以显著减少启动时间。你可能在想我要一直启用它,或者在想为什么spring家族没有默认启用延迟初始化。延迟初始化有一些缺点,如果延迟初始话对你意义重大,那么最好选择使用它.
由于不再加载类,直到需要时才会创建bean,所以延迟初始化可能会掩盖以前在启动时就已经确定的问题。此类问题可能包括no class def found错误、out of memory错误和由于错误的配置而导致的失败。
在Web应用程序中,延迟初始化可能导致触发Bean初始化的HTTP请求增加延迟,这通常只是第一个请求会这样,但它可能会对负载均衡和自动扩展会产生负面影响。
在web应用程序中,延迟初始化会导致触发bean初始化的HTTP请求的延迟增加。这通常只是第一个请求,但它可能会对负载均衡和自动扩展产生不利影响。
这个配置开启了吗?
如果您不确定延迟初始化对您的应用程序有什么影响,或者您想验证另一个框架的行为是否满足您的需求并匹配其声明,那么使用调试器可能会提供信息。通过在其中一个bean的构造函数中放置一个断点,您可以看到它何时被初始化。例如,在启用了延迟初始化的Spring Boot web应用程序中,您将看到直到向Spring MVC的DispatcherServlet或Spring WebFlux的DispatcherHandler发出第一个请求时才会创建@Controller bean。
何时启用延迟初始化
正如我们在上面看到的,延迟初始化可以在启动时间上提供显著的改进,但是也有一些显著的缺点,谨慎地启用它非常重要。
在开发过程中,延迟初始化是一个非常有益的领域,即使有缺点,也是非常少的。当你在一个应用程序需要迭代时,延迟初始化和DevTools的热重启所提供的缩短的启动时间可以显著提高您的工作效率。
另一个可以从延迟初始化中获益的领域是应用程序的集成测试。你可能已经在使用Spring Boot的测试片,通过限制在某些类型的测试中初始化的bean的数量来减少测试执行时间。延迟初始化提供了另一种机制来实现类似的最终结果。如果您不能将应用程序的结构设置为适合测试切片,或者对于特定类型的测试没有可用的切片,则启用延迟初始化将限制初始化的bean的数量,使之只适用于测试所需的bean。这将减少测试执行时间,特别是在开发期间单独运行测试时。
最后,您可能需要考虑在生产中启用延迟初始化。如果你这样做了,就应该小心行事。对于web应用程序,容器编排可能受益于能够更快响应的/health
端点,但是您还需要注意,当向应用程序自己的端点之一发出第一个请求时,可能会增加延迟。您还应该注意调整应用程序的JVM大小,禁用延迟初始化,以避免在使用了应用程序的所有组件之后出现任何不必要的内存溢出错误。