How to Cut Spring Boot Startup Time from 7 Minutes to 40 Seconds
This article explains why a SpringBoot service took 6‑7 minutes to start, shows how to pinpoint the bottlenecks using SpringApplicationRunListener and BeanPostProcessor, and presents concrete optimizations—reducing scan paths, manually registering beans, and fixing cache auto‑configuration—to shrink the local startup time to about 40 seconds.
Background
During daily development a SpringBoot project took 6‑7 minutes to expose its port. By debugging SpringApplicationRunListener and BeanPostProcessor it was discovered that the bean‑scanning and bean‑injection phases caused the major slowdown.
The following knowledge points are covered:
Observing the SpringBoot run method via SpringApplicationRunListener;
Monitoring bean‑injection time with BeanPostProcessor;
SpringBoot cache auto‑configuration principles;
Starter‑based auto‑configuration and custom starter creation.
Investigating Startup Time
Two main investigation directions were identified:
Trace the SpringBoot service startup process;
Trace bean‑initialization time.
Observing the SpringBoot run Method
The project uses an internal micro‑service component XxBoot whose startup flow mirrors SpringBoot: constructing an ApplicationContext and then invoking its run method.
public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }The run method is divided into several stages defined by the SpringApplicationRunListener interface:
public interface SpringApplicationRunListener { void starting(); void environmentPrepared(ConfigurableEnvironment env); void contextPrepared(ConfigurableApplicationContext ctx); void contextLoaded(ConfigurableApplicationContext ctx); void started(ConfigurableApplicationContext ctx); void running(ConfigurableApplicationContext ctx); void failed(ConfigurableApplicationContext ctx, Throwable ex); }SpringBoot provides a single default implementation EventPublishingRunListener that publishes events at each stage. By adding a custom implementation of SpringApplicationRunListener and registering it in META-INF/spring.factories, timestamps can be logged at the end of each stage to locate the most time‑consuming phases.
Monitoring Bean Initialization Time
The BeanPostProcessor interface allows hooking before and after bean initialization. By recording the start time in postProcessBeforeInitialization and computing the elapsed time in postProcessAfterInitialization, the initialization cost of each bean can be measured.
@Component public class TimeCostBeanPostProcessor implements BeanPostProcessor { private Map<String, Long> costMap = new ConcurrentHashMap<>(); @Override public Object postProcessBeforeInitialization(Object bean, String beanName) { costMap.put(beanName, System.currentTimeMillis()); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) { Long start = costMap.get(beanName); if (start != null) { long cost = System.currentTimeMillis() - start; if (cost > 0) { costMap.put(beanName, cost); System.out.println("bean: " + beanName + "\ttime: " + cost); } } return bean; } }Running this processor revealed a bean that took 43 seconds because it queried a large amount of configuration data from the database and wrote it to Redis.
Optimization Strategies
How to Reduce Excessive Scan Paths
Instead of scanning large third‑party packages, explicitly register only the required beans via JavaConfig. For example, replace the scan of com.xxx.ad.upm with a configuration class that defines the needed UpmResourceClient bean:
@Configuration public class ThirdPartyBeanConfig { @Bean public UpmResourceClient upmResourceClient() { return new UpmResourceClient(); } }This eliminates unrelated beans (services, controllers) from the application context, reduces memory usage, and shortens startup time.
How to Address Slow Bean Initialization
For beans that perform heavy work during initialization, consider asynchronous execution or lazy loading. In the example, the slow bean that loads configuration data could be refactored to load data in a background thread after the application has started.
Understanding SpringBoot Cache Auto‑Configuration
SpringBoot’s @EnableAutoConfiguration imports a series of auto‑configuration classes listed in META-INF/spring.factories. The cache subsystem is configured by CacheAutoConfiguration, which imports CacheConfigurationImportSelector. This selector chooses one concrete cache configuration based on the available dependencies; in the project it selects RedisCacheConfiguration, which creates a RedisCacheManager.
If a custom cache component’s package is removed from the scan, SpringBoot still creates its own RedisCacheManager because the auto‑configuration class is annotated with @ConditionalOnMissingBean(CacheManager.class). Therefore the missing custom bean is silently replaced by the default one.
Using a Starter for the Cache Component
To make the custom cache component “plug‑and‑play”, add a META-INF/spring.factories entry under
org.springframework.boot.autoconfigure.EnableAutoConfigurationthat points to the component’s configuration class (e.g., com.xxx.ad.rediscache.XxxAdCacheConfiguration). This lets SpringBoot import the custom configuration automatically without scanning the whole package.
Result
After applying the above optimizations the local startup time dropped from roughly 7 minutes to about 40 seconds, a dramatic improvement.
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.
macrozheng
Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.
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.
