Cutting Spring Boot Startup Time by 50%: From 280 s to 159 s
This article systematically analyzes a Spring Boot project's 280‑second startup, identifies bottlenecks such as bean initialization and sharding data source loading, and applies listener‑based timing, bean‑post‑processor profiling, and asynchronous initialization to reduce launch time to 159 seconds, improving developer efficiency.
Introduction
The project’s Spring Boot application suffered from a 280‑second startup due to an ever‑growing number of dependencies, which forced the Spring container to load and autowire many components. Long restart times hindered development and testing cycles.
Environment
Spring version: 4.3.22
Spring Boot version: 1.5.19
CPU: i5‑9500
Memory: 24 GB
Startup time before optimization: 280 seconds
Spring Boot Startup Process
The main startup logic resides in
org.springframework.boot.SpringApplication#run(String... args). The method creates a StopWatch, prepares the environment, prints the banner, creates the application context, refreshes it, and finally stops the watch.
public ConfigurableApplicationContext run(String... args) {<br/> StopWatch stopWatch = new StopWatch();<br/> stopWatch.start();<br/> // ... initialization steps ...<br/> refreshContext(context);<br/> afterRefresh(context, applicationArguments);<br/> stopWatch.stop();<br/> return context;<br/>}Listeners are invoked at multiple lifecycle stages, providing an extension point for custom timing.
Custom SpringApplicationRunListener
By implementing SpringApplicationRunListener and registering it in META-INF/spring.factories, we can log the duration of each startup phase.
@Slf4j<br/>public class MySpringApplicationRunListener implements SpringApplicationRunListener {<br/> private Long startTime;<br/> @Override public void starting() { startTime = System.currentTimeMillis(); log.info("Start {}", LocalTime.now()); }<br/> @Override public void environmentPrepared(ConfigurableEnvironment env) { log.info("Env prepared {} ms", System.currentTimeMillis() - startTime); startTime = System.currentTimeMillis(); }<br/> // other callbacks omitted for brevity<br/>}Identifying Slow Beans
Implementing InstantiationAwareBeanPostProcessor allows us to record the time before bean instantiation and after initialization, printing beans whose creation exceeds a threshold.
@Service<br/>public class TimeCostCalBeanPostProcessor implements InstantiationAwareBeanPostProcessor {<br/> private Map<String, Long> costMap = Maps.newConcurrentMap();<br/> @Override public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {<br/> costMap.putIfAbsent(beanName, System.currentTimeMillis());<br/> return null;<br/> }<br/> @Override public Object postProcessAfterInitialization(Object bean, String beanName) {<br/> Long start = costMap.get(beanName);<br/> long cost = System.currentTimeMillis() - start;<br/> if (cost > 5000) { System.out.println("bean: " + beanName + "\t time: " + cost + "ms"); }<br/> return bean;<br/> }<br/>}Profiling revealed that refreshContext() and, specifically, finishBeanFactoryInitialization() were the main culprits, taking up to 285 seconds.
Optimizing Sharding DataSource Loading
The singletonDataSource bean creates a sharding data source. Its initialization traverses all database tables to load metadata, causing a linear increase in startup time with the number of tables.
@Bean(name = "singletonDataSource")<br/>public DataSource singletonDataSource(DefaultDataSourceWrapper wrapper) throws SQLException {<br/> wrapper.getMaster().init();<br/> Map<String, DataSource> map = new HashMap<>();<br/> map.put("ds0", wrapper.getMaster());<br/> DataSource shardingDataSource = ShardingDataSourceFactory.createDataSource(map, shardingRuleConfiguration, prop);<br/> return shardingDataSource;<br/>}Reducing the number of sharding tables in the test environment or loading metadata lazily cuts a large portion of the delay.
Asynchronous Initialization
Beans such as ActivityServiceImpl perform heavy data queries in afterPropertiesSet(). By moving these calls to a custom thread pool, the overall startup time drops significantly.
public class AsyncInitListableBeanFactory extends DefaultListableBeanFactory {<br/> @Override protected void invokeInitMethods(String beanName, Object bean, RootBeanDefinition mbd) throws Throwable {<br/> if (beanName.equals("activityServiceImpl")) {<br/> AsyncTaskExecutor.submitTask(() -> super.invokeInitMethods(beanName, bean, mbd));<br/> } else {<br/> super.invokeInitMethods(beanName, bean, mbd);<br/> }<br/> }<br/>}An ApplicationContextInitializer swaps the default bean factory with the asynchronous version before the context refresh.
public class AsyncBeanFactoryInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {<br/> @Override public void initialize(ConfigurableApplicationContext ctx) {<br/> if (ctx instanceof GenericApplicationContext) {<br/> AsyncInitListableBeanFactory asyncFactory = new AsyncInitListableBeanFactory(ctx.getBeanFactory());<br/> Field f = GenericApplicationContext.class.getDeclaredField("beanFactory");<br/> f.setAccessible(true);<br/> f.set(ctx, asyncFactory);<br/> }<br/> }<br/>}A listener waits for all async tasks to finish before the application is considered fully started, preventing potential null‑pointer issues.
public class AsyncInitCompletionListener implements ApplicationListener<ContextRefreshedEvent>, ApplicationContextAware, PriorityOrdered {<br/> private ApplicationContext current;<br/> @Override public void setApplicationContext(ApplicationContext ctx) { this.current = ctx; }<br/> @Override public void onApplicationEvent(ContextRefreshedEvent e) {<br/> if (e.getApplicationContext() == current) { AsyncBeanInitExecutor.waitForInitTasks(); }<br/> }<br/> @Override public int getOrder() { return Ordered.HIGHEST_PRECEDENCE; }<br/>}Results
After applying bean‑level profiling, sharding data source tuning, and asynchronous initialization, the startup time dropped from 280 seconds to 159 seconds, a roughly 50 % improvement, greatly enhancing daily development and testing efficiency.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
vivo Internet Technology
Sharing practical vivo Internet technology insights and salon events, plus the latest industry news and hot conferences.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
