我尝试在Spring Boot 2.2.0中使用延迟初始化
Spring Boot版本2.2.0
最新版本的Spring Boot v2.2.0已经发布并成为了GA。
因此,我們將對其中一個功能LazyInitialization進行檢驗。
Spring Boot 2.2.0
发布 v2.2.0.RELEASE · spring-projects/spring-boot · GitHub
Spring Boot 2.2 发行说明 · spring-projects/spring-boot Wiki · GitHub
Spring Boot 参考文档
环境
-
- Windows10
-
- OracleJDK 13(build 13)
- Spring Boot 2.2.0 RELEAS
建立项目
“将 Project 设定为 Spring Initializr 的生成。”
Gradle 是一种构建工具。
我已确认在使用Spring Initializr创建的Gradle项目中应用了Gradle 5.6.2。
> gradlew -version
------------------------------------------------------------
Gradle 5.6.2
------------------------------------------------------------
Build time: 2019-09-05 16:13:54 UTC
Revision: 55a5e53d855db8fc7b0e494412fc624051a8e781
Kotlin: 1.3.41
Groovy: 2.5.4
Ant: Apache Ant(TM) version 1.9.14 compiled on March 12 2019
JVM: 13-ea (Oracle Corporation 13-ea+33)
OS: Windows 10 10.0 amd64
懒加载
Lazy Initialization是指在调用了目标Bean之后,才生成由ApplicationContext管理的Bean,而不是在应用程序启动时(即ApplicationContext初始化时)进行生成。
这个机制在之前的版本中也存在,但是从v2.2.0开始,通过将propertyspring.main.lazy-initialization设置为true,可以统一应用延迟初始化。
- application.yml
spring:
main:
lazy-initialization: true
- Bean class
package jp.co.musako.domain.model;
public class LazyInitializationDemoBean {
private String message;
public LazyInitializationDemoBean(String message) {
this.message = message;
System.out.println("call constructor " + this.message);
}
public void writer(String message){
System.out.println(this.message + " is " + message);
}
}
- config class
package jp.co.musako.config;
import jp.co.musako.domain.model.LazyInitializationDemoBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class Config {
@Bean("lazyInitializationDemoBean1")
public LazyInitializationDemoBean lazyInitializationDemoBean1(){
return new LazyInitializationDemoBean("create lazyInitializationDemoBean1");
}
@Bean("lazyInitializationDemoBean2")
public LazyInitializationDemoBean lazyInitializationDemoBean2(){
return new LazyInitializationDemoBean("create lazyInitializationDemoBean2");
}
}
- main class
package jp.co.musako;
import jp.co.musako.domain.model.LazyInitializationDemoBean;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Application.class, args);
System.out.println("Initialize Application Context");
LazyInitializationDemoBean bean1 = ctx.getBean("lazyInitializationDemoBean1", LazyInitializationDemoBean.class);
bean1.writer("first bean");
LazyInitializationDemoBean bean2 = ctx.getBean("lazyInitializationDemoBean2", LazyInitializationDemoBean.class);
bean2.writer("second bean");
}
}
运行结果
>gradlew bootRun
> Task :bootRun
2019-10-23 18:16:37.620 INFO 12708 --- [ main] jp.co.musako.Application : Started Application in 4.295 seconds (JVM running for 4.933)
Initialize Application Context
call constructor lazyInitializationDemoBean1
lazyInitializationDemoBean1 is first bean
call constructor lazyInitializationDemoBean2
lazyInitializationDemoBean2 is second bean
在这里,可以看出以下步骤的处理。
-
- 应用程序启动
在启动后输出”初始化应用程序上下文”
生成Bean
在Bean的构造函数中输出”调用构造函数lazyInitializationDemoBean1″
调用生成的Bean的writer方法
在writer方法中输出”lazyInitializationDemoBean1是第一个Bean”
懒加载的例外应用
在项目中应用LazyInitialization并将特定的Bean排除在LazyInitialization之外,您可以采取以下方式设置spring.main.lazy-initialization=true。
-
- 将@org.springframework.context.annotation.Lazy(false)设置为Bean
懒加载 (Spring Framework 5.2.0.RELEASE API)
在org.springframework.boot.LazyInitializationExcludeFilter中注册要排除的Bean
懒加载排除过滤器 (Spring Boot Docs 2.2.0.BUILD-SNAPSHOT API)
- config class
package jp.co.musako.config;
import jp.co.musako.domain.model.ExcludeLazyInitializationDemoBean;
import jp.co.musako.domain.model.LazyInitializationDemoBean;
import org.springframework.boot.LazyInitializationExcludeFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
@Configuration
public class Config {
@Bean
static LazyInitializationExcludeFilter integrationLazyInitExcludeFilter() {
return LazyInitializationExcludeFilter.forBeanTypes(ExcludeLazyInitializationDemoBean.class);
}
// Lazy Initialization適用対象
@Bean("lazyInitializationDemoBean1")
public LazyInitializationDemoBean lazyInitializationDemoBean1(){
return new LazyInitializationDemoBean("lazyInitializationDemoBean1");
}
// Lazy Initialization適用対象外
@Bean("lazyInitializationDemoBean2")
@Lazy(false)
public LazyInitializationDemoBean lazyInitializationDemoBean2(){
return new LazyInitializationDemoBean("lazyInitializationDemoBean2");
}
// Lazy Initialization適用対象外
@Bean
public ExcludeLazyInitializationDemoBean ExcludeLazyInitializationDemoBean(){
return new ExcludeLazyInitializationDemoBean("excludeLazyInitializationDemoBean");
}
}
- main class
package jp.co.musako;
import jp.co.musako.domain.model.ExcludeLazyInitializationDemoBean;
import jp.co.musako.domain.model.LazyInitializationDemoBean;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Application.class, args);
System.out.println("Initialize Application Context");
LazyInitializationDemoBean bean1 = ctx.getBean("lazyInitializationDemoBean1", LazyInitializationDemoBean.class);
bean1.writer("first bean");
LazyInitializationDemoBean bean2 = ctx.getBean("lazyInitializationDemoBean2", LazyInitializationDemoBean.class);
bean2.writer("second bean");
ExcludeLazyInitializationDemoBean ExcludeLazyInitializationBean = ctx.getBean("ExcludeLazyInitializationDemoBean", ExcludeLazyInitializationDemoBean.class);
ExcludeLazyInitializationBean.writer("third bean");
}
}
- 実行結果
>gradlew bootRun
> Task :bootRun
call constructor lazyInitializationDemoBean2
call constructor excludeLazyInitializationDemoBean
2019-10-23 18:52:52.464 INFO 6004 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2019-10-23 18:52:52.468 INFO 6004 --- [ main] jp.co.musako.Application : Started Application in 2.978 seconds (JVM running for 3.454)
Initialize Application Context
call constructor lazyInitializationDemoBean1
lazyInitializationDemoBean1 is first bean
lazyInitializationDemoBean2 is second bean
excludeLazyInitializationDemoBean is third bean
如上所示,在Config.java文件中定义的三个Bean中,我们可以看到只有lazyInitializationDemoBean1会被懒加载。
这里有源代码:GitHub – forests-k/spring-boot-lazy-initialization
Spring Boot v2.2.0的延迟初始化示例
在应用懒惰初始化时需要考虑的问题
引入惰性初始化的优点在于提高应用程序启动速度,并在需要时进行初始化,不浪费内存。
然而,在必要时初始化Bean并将其保存在容器中,这会导致以前的Bean生命周期的某些变化,而且无法预见容器中最大可能保持的Bean数量(即占用的内存量),这可能会对CPU负载和负载平均值产生影响,因此不能过分依赖LazyInitialization,这是有可能的。
因此,似乎应该考虑在测试环境或类似生产环境中进行一些测试,例如暂时禁用延迟初始化。
盗窃是一种犯罪行为,指的是非法获取他人财物。
-
- Lazy Initialization in Spring Boot 2.2
-
- Spring Boot Reference Documentation
- appendix Common Application properties