环境:Spring6.2.0-SNAPSHOT
1. 简介
在最新的Spring6.2.0-SNAPSHOT版本中,一项引人注目的新功能被引入——Parallel Bean Initialization during Startup,即启动过程中的并行Bean初始化。此功能旨在显著提升Spring应用程序的启动速度,为开发者带来更为流畅的开发体验。
在传统的Spring应用程序中,Bean的初始化通常是按照特定的顺序进行的,这在一定程度上限制了启动过程的并行性,影响了启动速度。然而,在Spring6.2.0-SNAPSHOT版本中,通过引入并行Bean初始化功能,Spring框架能够同时初始化多个Bean,从而显著减少启动时间。
此项功能实现基于精心设计的并发控制机制,确保Bean之间的依赖关系得到正确维护,同时最大限度地提高并行度。这意味着,即使存在复杂的Bean依赖关系,Spring也能够有效地管理并行初始化过程,避免潜在的初始化冲突或错误。
2. 实战案例
2.1 环境准备
static class PersonService {
public PersonService() {
try {
TimeUnit.SECONDS.sleep(3) ;
} catch (InterruptedException e) {}
System.out.println("PersonService init ...") ;
}
}
static class CommonService {
public CommonService() {
try {
TimeUnit.SECONDS.sleep(3) ;
} catch (InterruptedException e) {}
System.out.println("CommonService init ...") ;
}
}
在上面的两个Service的构造方法中,分别模拟了耗时的操作。
@Configuration
static class AppConfig {
@Bean
public PersonService personService() {
return new PersonService() ;
}
@Bean
public CommonService commonService() {
return new CommonService() ;
}
}
2.2 传统Bean初始化
StopWatch watch = new StopWatch("初始化容器") ;
watch.start() ;
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class)) {
}
watch.stop();
System.out.println(watch.prettyPrint()) ;
输出结果
PersonService init ...
CommonService init ...
StopWatch '初始化容器': 6.3145531 seconds
----------------------------------------
Seconds % Task name
----------------------------------------
6.3145531 100%
整体耗时:6.3s。
2.3 并发Bean初始化
这里首先需要修改@Bean定义配置
// 设置Bean的初始化在后台运行
@Bean(bootstrap = Bootstrap.BACKGROUND)
public PersonService personService() {
return new PersonService() ;
}
@Bean(bootstrap = Bootstrap.BACKGROUND)
public CommonService commonService() {
return new CommonService() ;
}
接着测试,输出结果
日志提示:Bean "commonService"标记为后台初始化,但未配置引导执行器-回退到主线初始化。根据提示我们还需要配置一个Executor类型的Bean。
@Bean
public Executor bootstrapExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(5) ;
taskExecutor.setMaxPoolSize(5) ;
taskExecutor.initialize() ;
return taskExecutor ;
}
接着测试,输出结果
CommonService init ...
PersonService init ...
StopWatch '初始化容器': 3.3203919 seconds
----------------------------------------
Seconds % Task name
----------------------------------------
3.3203919 100%
整体耗时:3.3s,说明我们Bean的初始化分别在不同的线程中执行。
这样一来,如果你项目中如果有很多比较耗时的操作,那么通过异步线程的方式,那将节省很多的时间。
注意:上面的示例是通过注解的方式定义bean,如果你使用BeanDefinition方式定义,那么你需要做如下配置
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) {
context.register(AppConfig.class) ;
context.registerBean(PersonDAO.class, bd -> {
if (bd instanceof AnnotatedGenericBeanDefinition bean) {
bean.setBackgroundInit(true) ;
}
}) ;
context.refresh() ;
}
以上是关于Spring6.2中如何配置Bean的并行处理。
3. 实现原理
Spring容器启动和兴方法refresh。
public abstract class AbstractApplicationContext {
public void refresh() {
finishBeanFactoryInitialization(beanFactory);
}
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// 初始化bootstrap executor
// 判断容器是否存在bootstrapExecutor为名的Bean,并且类型是Executor
if (beanFactory.containsBean(BOOTSTRAP_EXECUTOR_BEAN_NAME) &&
beanFactory.isTypeMatch(BOOTSTRAP_EXECUTOR_BEAN_NAME, Executor.class)) {
// 设置到当前的BeanFactory中,后面实例化时使用
beanFactory.setBootstrapExecutor(
beanFactory.getBean(BOOTSTRAP_EXECUTOR_BEAN_NAME, Executor.class)
);
}
// ...
// 实例化非延迟初始化单例Bean
beanFactory.preInstantiateSingletons();
}
}
实例化单例Bean
public class DefaultListableBeanFactory {
public void preInstantiateSingletons() {
List<CompletableFuture<?>> futures = new ArrayList<>();
for (String beanName : beanNames) {
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
if (!mbd.isAbstract() && mbd.isSingleton()) {
// 实例化Bean
CompletableFuture<?> future = preInstantiateSingleton(beanName, mbd);
if (future != null) {
futures.add(future);
}
}
}
// 等待所有的bean都完成
CompletableFuture.allOf(futures.toArray(new CompletableFuture<?>[0])).join();
}
private CompletableFuture<?> preInstantiateSingleton(String beanName, RootBeanDefinition mbd) {
// 是否支持后台运行
if (mbd.isBackgroundInit()) {
Executor executor = getBootstrapExecutor();
if (executor != null) {
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
getBean(dep);
}
}
CompletableFuture<?> future = CompletableFuture.runAsync(
() -> instantiateSingletonInBackgroundThread(beanName), executor);
addSingletonFactory(beanName, () -> {
try {
future.join();
}
return future;
});
return (!mbd.isLazyInit() ? future : null);
}
}
// 回退到main线程执行
if (!mbd.isLazyInit()) {
instantiateSingleton(beanName);
}
return null;
}
}
整体的处理方式还是比较简单的。
以上是本篇文章的全部内容,如对你有帮助就请作者吃个棒棒糖🍭
完毕!!!