在Spring6.2版本后,支持了异步初始化。
什么是异步初始化?见名知意,就是将Spring项目的初始化过程中的Bean通过异步加载的方式提高启动速度。
在业务系统中通常启动不会特别耗时,但也可以将系统启动过程中需要耗时初始化的Bean通过异步将其并行初始化,节省部分启动时间。本文作为学习Why哥文章之后的简单使用记录。
模拟初始化Bean耗时
- ABean
@Slf4j
public class TestABean {
@SneakyThrows
public TestABean() {
log.info("A Bean开始初始化");
TimeUnit.SECONDS.sleep(5);
log.info("A Bean初始化完成");
}
}
- BBean
@Slf4j
public class TestBBean {
@SneakyThrows
public TestBBean() {
log.info("B Bean开始初始化");
TimeUnit.SECONDS.sleep(6);
log.info("B Bean初始化完成");
}
}
- 配置类
@Configuration
public class RegisterConfiguration {
@Bean
public TestABean testABean() {
return new TestABean();
}
@Bean
public TestBBean testBBean() {
return new TestBBean();
}
}
- 启动类
@Slf4j
@ComponentScan("top.imyzt.learning.spring.startup")
public class Application {
public static void main(String[] args) {
StopWatch stopWatch = new StopWatch("Spring启动");
stopWatch.start();
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Application.class);
stopWatch.stop();
System.out.println(stopWatch.prettyPrint());
}
}
- 启动日志
21:39:33.658 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'testABean'
21:39:33.663 [main] INFO top.imyzt.learning.spring.startup.core.TestABean - A Bean开始初始化
21:39:38.667 [main] INFO top.imyzt.learning.spring.startup.core.TestABean - A Bean初始化完成
21:39:38.669 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'testBBean'
21:39:38.670 [main] INFO top.imyzt.learning.spring.startup.core.TestBBean - B Bean开始初始化
21:39:44.673 [main] INFO top.imyzt.learning.spring.startup.core.TestBBean - B Bean初始化完成
StopWatch 'Spring启动': 11.555947229 seconds
------------------------------------------
Seconds % Task name
------------------------------------------
11.55594723 100%
- 小结
可以看到,在常规情况下,A和B Bean是串行初始化的,整个初始化耗时11.5s。
异步初始化
在Spring6.2版本中,@Bean
注解引入了一个新的属性:bootstrap
,默认Bean.Bootstrap.DEFAULT
时为串行初始化,当指定为Bean.Bootstrap.BACKGROUND
时,Spring会尝试异步初始化该Bean,但是需要配置一个名为bootstrapExecutor
的线程池,用作异步初始化时所需的线程。
只需要将配置类稍作修改,就可以将指定的Bean进行异步初始化:
@Configuration
public class RegisterConfiguration {
@Bean(bootstrap = Bean.Bootstrap.BACKGROUND)
public TestABean testABean() {
return new TestABean();
}
@Bean(bootstrap = Bean.Bootstrap.BACKGROUND)
public TestBBean testBBean() {
return new TestBBean();
}
@Bean()
public Executor bootstrapExecutor() {
return new ThreadPoolExecutor(2, 10, 1, TimeUnit.MINUTES, new ArrayBlockingQueue<>(1024));
}
}
然后我们再查看启动效果:
21:53:49.023 [pool-1-thread-1] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'testABean'
21:53:49.024 [pool-1-thread-1] INFO top.imyzt.learning.spring.startup.core.TestABean - A Bean开始初始化
21:53:49.025 [pool-1-thread-2] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'testBBean'
21:53:49.026 [pool-1-thread-2] INFO top.imyzt.learning.spring.startup.core.TestBBean - B Bean开始初始化
21:53:54.029 [pool-1-thread-1] INFO top.imyzt.learning.spring.startup.core.TestABean - A Bean初始化完成
21:53:55.031 [pool-1-thread-2] INFO top.imyzt.learning.spring.startup.core.TestBBean - B Bean初始化完成
StopWatch 'Spring启动': 6.740055683 seconds
-----------------------------------------
Seconds % Task name
-----------------------------------------
6.740055683 100%
可以看到启动时间从11.5s降为6.7s,效果十分显著,在部分需要依赖外部或已知需要耗时初始化的Bean,可以通过此方法进行优化。“我可以不用,你不能没有”。