背景
在某厂实习的时候,有些需求需要本地debug来确保bug free。但是会发现本地跑项目,至少需要等20几秒,有点bug 修改后又重启,又修改,又重启,极度浪费时间。
所以第一时间怀疑是Spring管理的Bean做了大量的初始化操作,Spring创建bean的过程是单线程,假如一个bean 初始化需要1s,那么 20个bean,总共创建就需要20s。
基于以上怀疑,翻了一下代码。发现了很多初始化操作(比如RPC拉xx信息放到本地缓存,查询MySQL xx信息放到本地缓存....)。
基于此,开发了Spring异步初始化组件来加速Spring 启动过程。
快速开始
1. 引入依赖
<dependency>
<groupId>com.hdu</groupId>
<artifactId>async-initialize-bean-starter</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
2. 启动类添加注解 @EnableAsyncInit
@SpringBootApplication
@EnableAsyncInit
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
3. 使用注解 @AsyncInit
将 @PostConstruct 替换为 @AsyncInit
@Component
public class Bean {
// @PostConstruct 替换为 @AsyncInit
@AsyncInit
public void init() {
try {
// 模拟耗时初始化操作,如RPC,数据库IO....
Thread.sleep(10000);
System.out.println("Bean init");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
这样 init 方法就会异步执行。Spring就能去执行下一个bean的创建过程了~使用起来贼简单
此外,你还可以给这个 init方法自定义线程池(自定义线程池可以做一些监控,动态设置参数等)。你只需要使用注解 @AsyncInit(executor = "myThreadPoolExecutor") 就可以指定自定义线程池(指定自定义线程池名字即可~)。如果你指定的线程池不存在,那么该 init() 方法还是会交给 Spring 容器来执行,此时这个注解就失去了异步执行的作用。
使用效果
@Component
@Slf4j
public class Bean {
@AsyncInit
public void init() {
try {
Thread.sleep(10000);
log.info("Bean init");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
还是上面这个耗时的Bean,我们分别观测一下 使用 @AsyncInit 和 使用 @PostConstruct吧
使用 @AsyncInit
我们可以发现,SpringBoot启动如此丝滑~,Bean耗时的初始化操作是异步执行的
不使用 @AsyncInit
我们可以发现,SpringBoot启动卡了10s。所以还不使用起来?