Backend Development 19 min read

Optimizing SpringBoot Startup Time: Analyzing Bean Scanning and Initialization Bottlenecks

This article investigates why a SpringBoot service takes 6‑7 minutes to start, identifies the bean‑scanning and bean‑initialization phases as the main performance culprits, and presents concrete JavaConfig and starter‑based solutions to reduce startup time to around 40 seconds while handling cache auto‑configuration pitfalls.

Architecture Digest
Architecture Digest
Architecture Digest
Optimizing SpringBoot Startup Time: Analyzing Bean Scanning and Initialization Bottlenecks

0 Background

The company's SpringBoot project suffered from extremely slow startup, often taking 6‑7 minutes to expose the port, which severely impacted development efficiency. By debugging SpringApplicationRunListener and BeanPostProcessor mechanisms, the author discovered major performance bottlenecks during the bean‑scanning and bean‑injection phases.

Using JavaConfig to register beans and optimizing third‑party dependencies reduced the local startup time from 7 minutes to roughly 40 seconds.

1 Time‑Consuming Issue Investigation

The investigation follows two main ideas: (1) examine the SpringBoot startup run method, and (2) monitor bean‑initialization time.

1.1 Observing 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);
    }
}

public static ConfigurableApplicationContext run(Class
[] primarySources, String[] args) {
    return new SpringApplication(primarySources).run(args);
}

The run method creates listeners, prepares the environment, creates the context, refreshes it, and finally calls the runners. The SpringApplicationRunListener interface defines callbacks for each stage (starting, environmentPrepared, contextPrepared, contextLoaded, started, running, failed).

public interface SpringApplicationRunListener {
    void starting();
    void environmentPrepared(ConfigurableEnvironment environment);
    void contextPrepared(ConfigurableApplicationContext context);
    void contextLoaded(ConfigurableApplicationContext context);
    void started(ConfigurableApplicationContext context);
    void running(ConfigurableApplicationContext context);
    void failed(ConfigurableApplicationContext context, Throwable exception);
}

By implementing a custom listener (e.g., MySpringApplicationRunListener ) and logging timestamps at each callback, the author measured the duration of each phase and identified that the longest delay occurs between contextLoaded and started , which corresponds to the refreshContext and afterRefresh calls.

@Slf4j
public class MySpringApplicationRunListener implements SpringApplicationRunListener {
    public MySpringApplicationRunListener(SpringApplication sa, String[] args) {}
    @Override public void starting() { log.info("starting {}", LocalDateTime.now()); }
    @Override public void environmentPrepared(ConfigurableEnvironment env) { log.info("environmentPrepared {}", LocalDateTime.now()); }
    @Override public void contextPrepared(ConfigurableApplicationContext ctx) { log.info("contextPrepared {}", LocalDateTime.now()); }
    @Override public void contextLoaded(ConfigurableApplicationContext ctx) { log.info("contextLoaded {}", LocalDateTime.now()); }
    @Override public void started(ConfigurableApplicationContext ctx) { log.info("started {}", LocalDateTime.now()); }
    @Override public void running(ConfigurableApplicationContext ctx) { log.info("running {}", LocalDateTime.now()); }
    @Override public void failed(ConfigurableApplicationContext ctx, Throwable ex) { log.info("failed {}", LocalDateTime.now()); }
}

1.2 Monitoring Bean initialization time

To pinpoint slow beans, the author leveraged BeanPostProcessor , which provides hooks before and after bean initialization. By recording the start time in postProcessBeforeInitialization and calculating the elapsed time in postProcessAfterInitialization , the initialization cost of each bean can be printed.

public interface BeanPostProcessor {
    default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; }
    default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; }
}
@Component
public class TimeCostBeanPostProcessor implements BeanPostProcessor {
    private Map
costMap = Maps.newConcurrentMap();
    @Override public Object postProcessBeforeInitialization(Object bean, String beanName) {
        costMap.put(beanName, System.currentTimeMillis());
        return bean;
    }
    @Override public Object postProcessAfterInitialization(Object bean, String beanName) {
        if (costMap.containsKey(beanName)) {
            long cost = System.currentTimeMillis() - costMap.get(beanName);
            if (cost > 0) {
                costMap.put(beanName, cost);
                System.out.println("bean: " + beanName + "\ttime: " + cost);
            }
        }
        return bean;
    }
}

Running the service with this processor revealed a bean that took 43 seconds to initialize because it queried a massive amount of configuration data from the database and wrote it to Redis. It also uncovered many third‑party service beans (e.g., UPM) that were unnecessary for the current functionality.

2 Optimization方案

2.1 Reducing excessive scan paths

The straightforward solution is to stop scanning large base packages and explicitly declare required beans via JavaConfig. For example, instead of scanning com.xxx.ad.upm , a configuration class manually creates the UpmResourceClient bean:

@Configuration
public class ThirdPartyBeanConfig {
    @Bean
    public UpmResourceClient upmResourceClient() {
        return new UpmResourceClient();
    }
}

This eliminates redundant beans, lowers memory consumption, and prevents unrelated services from being injected.

2.2 Solving high bean‑initialization cost

For beans that are inherently slow (e.g., loading massive metadata), the author suggests asynchronous initialization or lazy loading, such as submitting the heavy task to a thread pool.

3 New Issue – Cache Component Failure

After the optimizations, the service started quickly, but during pre‑release testing the Redis cache component stopped working. The component was originally introduced by scanning com.xxx.ad.rediscache , which registers a custom CacheManager . When the scan path was removed, SpringBoot’s own auto‑configuration created a default RedisCacheManager , causing the application to use an unexpected cache manager.

3.1 SpringBoot auto‑configuration pitfalls

SpringBoot’s @EnableAutoConfiguration (included in @SpringBootApplication ) imports a series of auto‑configuration classes via spring.factories . The CacheAutoConfiguration eventually imports RedisCacheConfiguration , which creates a RedisCacheManager when the conditions are met. Because the project also enables caching with @EnableCaching , SpringBoot supplies a default manager when the custom component is not scanned.

3.2 Using starter mechanism for the cache component

Instead of manually scanning the component’s package, the author recommends turning the cache library into a SpringBoot starter. By adding an entry to META-INF/spring.factories : # EnableAutoConfigurations org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.xxx.ad.rediscache.XxxAdCacheConfiguration the custom configuration is automatically imported, preserving the intended CacheManager without requiring a full package scan.

Overall, the article demonstrates a systematic approach to diagnosing SpringBoot startup slowness, applying targeted JavaConfig and starter techniques, and understanding the interplay between manual bean registration and SpringBoot’s auto‑configuration.

performanceCacheSpringBootAutoConfigurationBeanScanningJavaConfig
Architecture Digest
Written by

Architecture Digest

Focusing on Java backend development, covering application architecture from top-tier internet companies (high availability, high performance, high stability), big data, machine learning, Java architecture, and other popular fields.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.