SpringBoot核心特性——延迟初始化

2023年 7月 19日 54.8k 0

前言

SpringBoot在启动的时候可以选择开启是否延迟Bean的初始化,若开启了此项配置,则在SpringApplication启动期间,只会根据需要创建好必须的Bean,而不是一口气将所有的Bean都创建好。而那些尚未创建的Bean,只有当有需要的时候才会进行相应的创建。通常情况下,这样有利于减少SpringApplication的启动时间,特别是当项目越来越多Bean之后...

新建一个简简单单的HelloController,代码如下:

package geek.springboot.application.controller;  
  
import lombok.extern.slf4j.Slf4j;  
import org.springframework.web.bind.annotation.GetMapping;  
import org.springframework.web.bind.annotation.RestController;  
  
import javax.annotation.PostConstruct;  
  
/**  
* Controller  
*  
* @author Bruse  
*/  
@Slf4j  
@RestController  
public class HelloController {  
  
    /**  
    * init方法会在HelloController被创建的时候调用  
    * 这里输出一行日志,以便观察HelloController具体是在什么时候创建的  
    */  
    @PostConstruct  
    public void init() {  
        log.info("Hello Controller init");  
    }  
  
    @GetMapping("/hello")  
    public String hello() {  
        return "hello";  
    }  
  
}

启动SpringApplication,控制台输出如下,说明默认情况下,SpringApplication延迟初始化功能是未开启的.

image.png

开启延迟初始化的三种方式

1. 配置文件里设置lazy-initialization属性

在项目resources文件夹下新建application.yml,并配置lazy-initialization,代码如下:

image.png

重新启动项目,控制台输出如下,会发现HelloController的init()并没有被调用,说明HelloController并未被创建,延迟加载已生效.

image.png

在这个时候通过HttpClient发起Get /hello请求,控制台输出如下:

image.png

可以看到只有当tomcat接收到/hello请求并需要进行处理时,SpringMVC的dispatcherServlet以及我们的HelloController才真正被创建并进行请求处理.

延迟加载的Bean,只有当需要它的时候,Spring才会真正创建它

2. 调用SpringApplication的setLazyInitialization()方法

代码如下:

package geek.springboot.application;  
  
import org.springframework.boot.SpringApplication;  
import org.springframework.boot.autoconfigure.SpringBootApplication;  
  
@SpringBootApplication  
public class Application {  
  
    public static void main(String[] args) {  
        SpringApplication springApplication = new SpringApplication();  
        // 开启延迟初始化
        springApplication.setLazyInitialization(true);  
        // 启动SpringApplication
        springApplication.run(Application.class, args);  
    }  
  
}

该配置方式与使用application.yml开启延迟加载本质是一样的,故不再进行相关贴图印证.

3. 调用SpringApplicationBuilder的lazyInitialization()方法

代码如下:

package geek.springboot.application;  
  
import org.springframework.boot.autoconfigure.SpringBootApplication;  
import org.springframework.boot.builder.SpringApplicationBuilder;  
  
@SpringBootApplication  
public class Application {  
  
public static void main(String[] args) {  
    SpringApplicationBuilder builder = new SpringApplicationBuilder();  
        // 开启延迟初始化  
        builder.lazyInitialization(true);  
        // 构建SpringApplication并启动  
        builder.build().run(Application.class, args);  
    }  
  
}

该配置方式与使用application.yml开启延迟加载本质是一样的,故不再进行相关贴图印证.

值得一提的是,使用SpringApplicationBuilder构建并启动SpringApplication的方式,其实是使用了23种设计模式之一的建造者模式,并且从源码中不难看出,调用SpringApplicationBuilder的lazyInitialization()方法,其实就是调用了SpringApplication的setLazyInitialization()方法.

源码见下图:

image.png

忽略延迟初始化

因为SpringBoot延迟初始化是全局的,但有些时候我们还是需要让某些Bean在启动的时候就创建好,那么可以给Bean加上@Lazy注解,value设置为false,表示该Bean不延迟初始化.

image.png

给HelloController添加上@Lazy(value = false)后,重启应用,可以看到HelloController在应用启动时就已经初始化.

image.png

延迟初始化的缺点

1. 推迟了Bean创建时的问题发现

在HellController中创建一个value属性,并添加上@Value注解,表示该value需要从配置文件中读取,代码如下:

image.png

再次请求/hello,可以看到因为并没有custome.value这个配置,所以value属性的值注入失败,HelloController初始化失败。控制台输出如下:

image.png

因为开启了延迟初始化的缘故,这个隐藏的问题只有当请求/hello的时候才暴露出来,而这种情况在生产环境下其实是不允许的,因为生产一贯的原则是有问题尽早发现,尽早失败,尽早处理。如果是非延迟初始化,那么在SpringApplication启动的时候程序就会报错启动不了,能让开发更早地发现并解决问题.

额外补充一点,如果项目依赖的Bean足够多,同时JVM最大内存设置得不够大的话,也需要当心,因为可能随着延迟初始化的Bean都被逐一创建,内存占用越来越多,最后可能因为JVM最大内存不够导致OOM。所以若开启延迟加载,JVM的最大内存也需要相应注意点.

总结

综上所述,其实开启延迟初始化,笔者是认为弊大于利的,因为它会隐藏了很多其实应该在程序运行一开始就应该被发现的问题,至于那一点点启动速度提升...如果真的启动很慢的话,更应该考虑是不是把太多无用的依赖精简掉,是否应该进行服务的拆分等.

相关文章

JavaScript2024新功能:Object.groupBy、正则表达式v标志
PHP trim 函数对多字节字符的使用和限制
新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
为React 19做准备:WordPress 6.6用户指南
如何删除WordPress中的所有评论

发布评论