在Spring6.2版本后,支持了异步初始化。
什么是异步初始化?见名知意,就是将Spring项目的初始化过程中的Bean通过异步加载的方式提高启动速度。
在业务系统中通常启动不会特别耗时,但也可以将系统启动过程中需要耗时初始化的Bean通过异步将其并行初始化,节省部分启动时间。本文作为学习Why哥文章之后的简单使用记录。

模拟初始化Bean耗时

  1. ABean
@Slf4j
public class TestABean {
    @SneakyThrows
    public TestABean() {
        log.info("A Bean开始初始化");
        TimeUnit.SECONDS.sleep(5);
        log.info("A Bean初始化完成");
    }
}
  1. BBean
@Slf4j
public class TestBBean {
    @SneakyThrows
    public TestBBean() {
        log.info("B Bean开始初始化");
        TimeUnit.SECONDS.sleep(6);
        log.info("B Bean初始化完成");
    }
}
  1. 配置类
@Configuration
public class RegisterConfiguration {
    @Bean
    public TestABean testABean() {
        return new TestABean();
    }
    @Bean
    public TestBBean testBBean() {
        return new TestBBean();
    }
}
  1. 启动类
@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());
    }
}
  1. 启动日志
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%    
  1. 小结
    可以看到,在常规情况下,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,可以通过此方法进行优化。“我可以不用,你不能没有”。

参考

  1. 13年过去了,Spring官方竟然真的支持Bean的异步初始化了!
  2. 博客对应代码