Spring异步初始化组件

618 阅读2分钟

背景

在某厂实习的时候,有些需求需要本地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

图片.png 我们可以发现,SpringBoot启动如此丝滑~,Bean耗时的初始化操作是异步执行的

不使用 @AsyncInit

图片.png 我们可以发现,SpringBoot启动卡了10s。所以还不使用起来?