Revolutionizing Application Launch: Multithreaded Bean Initialization in Spring 6.2
Written on
Chapter 1: Introduction to Spring 6.2
Spring 6.2.0 has introduced an exciting feature known as Parallel Bean Initialization, designed to dramatically improve the startup speed of applications. This advancement allows multiple Beans to be initialized at the same time during the startup phase, significantly cutting down on the overall launch time. Through well-structured concurrency control, Spring effectively manages Bean dependencies while maximizing parallel execution. This means even intricate Bean dependency structures can be handled efficiently, mitigating initialization conflicts or errors. Experience the future of rapid application launches with Spring 6.2!
Section 1.1: Understanding the Feature
Historically, Spring applications would initialize Beans in a predetermined order, which limited the potential for parallel processing and consequently slowed down the startup time. With the advent of Spring 6.2.0, the framework facilitates simultaneous Bean initialization, leading to a notable reduction in startup duration.
The implementation relies on a meticulously crafted concurrency control system that maintains the necessary dependencies among Beans while promoting parallelism. Thus, even with complex interdependencies, the Spring framework can adeptly manage the initialization process, preventing conflicts or issues.
Section 1.2: Practical Implementation
Before utilizing this feature, ensure that your project is configured to use Spring Framework 6.2 in your pom.xml file. This is crucial to avoid issues with missing dependencies. To set this up, specify the Spring-bom version number ahead of your dependencies as follows:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-framework-bom</artifactId>
<version>6.2.0-M1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
Subsection 1.2.1: Setting Up the Environment
Here’s how to define two services that simulate time-consuming tasks:
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.TimeUnit;
@Slf4j
public class CommonService {
public CommonService() {
try {
TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {
Thread.currentThread().interrupt();}
log.info("CommonService initialized ...");
}
}
@Slf4j
public class OrderService {
public OrderService() {
try {
TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {
Thread.currentThread().interrupt();}
log.info("OrderService initialized ...");
}
}
Both constructors simulate lengthy operations. Next, configure the application context:
@Configuration
public class AppConfig {
@Bean
public OrderService orderService() {
return new OrderService();}
@Bean
public CommonService commonService() {
return new CommonService();}
}
Section 1.3: Traditional vs. Concurrent Bean Initialization
To compare traditional Bean initialization, use the following code:
@SpringBootApplication
@Slf4j
public class SpringTestApp {
public static void main(String[] args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start("Spring Startup");
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
stopWatch.stop();
log.info(stopWatch.prettyPrint());
context.close();}
}
Output:
09:58:17.286 [main] INFO com.example.springtest.service.OrderService -- OrderService initialized ...
09:58:20.306 [main] INFO com.example.springtest.service.CommonService -- CommonService initialized ...
09:58:20.368 [main] INFO com.example.springtest.SpringTestApp -- Stopwatch '': 6.925379375 seconds
This indicates a total startup time of 6.9 seconds.
Now, let's implement concurrent Bean initialization with modifications in the @Bean definitions:
@Configuration
public class AppConfig {
@Bean(bootstrap = Bean.Bootstrap.BACKGROUND)
public OrderService orderService() {
return new OrderService();}
@Bean(bootstrap = Bean.Bootstrap.BACKGROUND)
public CommonService commonService() {
return new CommonService();}
@Bean
public Executor bootstrapExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(5);
taskExecutor.setMaxPoolSize(5);
taskExecutor.initialize();
return taskExecutor;
}
}
This configuration enables background multithreaded loading of Beans. Without setting the executor, the application will revert to main thread execution, negating the benefits of this enhancement.
Output:
10:36:57.803 [bootstrapExecutor-2] INFO com.example.springtest.service.CommonService -- CommonService initialized ...
10:36:57.803 [bootstrapExecutor-1] INFO com.example.springtest.service.OrderService -- OrderService initialized ...
10:36:57.982 [main] INFO com.example.springtest.SpringTestApp -- Stopwatch '': 3.784657208 seconds
This shows a total startup time of just 3.7 seconds, effectively doubling the speed of initialization thanks to the use of asynchronous threads.
Chapter 2: Understanding the Implementation Principles
The Spring container's startup process and the new refresh method are crucial to grasping this feature.
public abstract class AbstractApplicationContext {
public void refresh() {
finishBeanFactoryInitialization(beanFactory);}
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
if (beanFactory.containsBean(BOOTSTRAP_EXECUTOR_BEAN_NAME) &&
beanFactory.isTypeMatch(BOOTSTRAP_EXECUTOR_BEAN_NAME, Executor.class)) {
beanFactory.setBootstrapExecutor(
beanFactory.getBean(BOOTSTRAP_EXECUTOR_BEAN_NAME, Executor.class));
}
beanFactory.preInstantiateSingletons();
}
}
In conclusion, the introduction of multithreaded Bean instantiation in Spring 6.2 significantly accelerates application startup. With its official release, many developers are expected to adopt this impressive feature.