Backend Development 20 min read

Optimizing SpringBoot Application Startup Speed: Diagnosis and Solutions

This article details how a SpringBoot advertising platform service with startup times of 400‑500 seconds was analyzed and optimized through Actuator monitoring, log inspection, Tomcat TLD scan disabling, asynchronous HBase warm‑up, custom BeanPostProcessors, async JSF consumer initialization, Tomcat version tuning, and hardware upgrades, achieving roughly a 60% reduction in launch time.

JD Tech
JD Tech
JD Tech
Optimizing SpringBoot Application Startup Speed: Diagnosis and Solutions

The article records the optimization measures taken before the 11.11 promotion to address the excessively slow startup of a SpringBoot advertising platform application, which required 400‑500 seconds plus image download time, leading to deployment times close to ten minutes and hampering rapid rollback and business continuity.

Problem background : the long startup caused delayed releases, lengthy container orchestration (over half an hour for hundreds of containers), and slow iteration in pre‑release environments, prompting an urgent need for speed improvements.

Solution overview : several diagnostic and remediation steps were applied, including using SpringBoot Actuator, analyzing startup logs, disabling Tomcat TLD scanning, making HBase initialization asynchronous, introducing custom BeanPostProcessors to measure bean initialization cost, refactoring JSF consumer creation to run in an async thread pool, handling Tomcat version differences, and moving the deployment to higher‑performance hardware.

1. SpringBoot Actuator – the startup endpoint provides bean‑level startup timing, though it may double‑count nested bean initialization.

2. Startup log analysis – by enabling debug logging, blank seconds in the log reveal blocking points such as Tomcat TLD scanning and HBase configuration loading.

3. Tomcat TLD scan optimization – the log line org.apache.jasper.servlet.TldScanner.scanJars: At least one JAR was scanned for TLDs yet none were found indicated unnecessary scanning; setting tomcat.util.scan.StandardJarScanFilter.jarsToSkip=*.jar in catalina.properties disables it.

4. HBase async warm‑up – the first request to HBase incurred a ~6 s delay; moving the warm‑up into a separate thread via SmartInitializingSingleton removes this block.

5. Custom BeanPostProcessor for cost measurement :

@Component
@Slf4j
public class TimeCostPriorityOrderedBeanPostProcessor implements BeanPostProcessor, PriorityOrdered {
    private Map
costMap = Maps.newConcurrentMap();
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        costMap.put(beanName, System.currentTimeMillis());
        return bean;
    }
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (costMap.containsKey(beanName)) {
            long cost = System.currentTimeMillis() - costMap.get(beanName);
            if (cost > 50) {
                log.info("==>Before method cost beanName:{}, cost:{}", beanName, cost);
            }
        }
        return bean;
    }
    @Override
    public int getOrder() { return Integer.MIN_VALUE; }
}

and a low‑order counterpart:

@Component
@Slf4j
public class ZBeanPostProcessor implements BeanPostProcessor {
    private Map
costMap = Maps.newConcurrentMap();
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        costMap.put(beanName, System.currentTimeMillis());
        return bean;
    }
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (costMap.containsKey(beanName)) {
            long cost = System.currentTimeMillis() - costMap.get(beanName);
            if (cost > 50) {
                log.info("==>After method cost beanName:{}, cost:{}", beanName, cost);
            }
        }
        return bean;
    }
}

These processors identified beans whose initialization exceeded 50 ms, most of which carried the @JsfConsumer annotation.

6. Async JSF consumer initialization – the refer call in ConsumerConfig.refer() was moved to a dedicated thread pool to avoid blocking the main startup thread:

public static final ThreadPoolExecutor CONSUMER_REFER = new ThreadPoolExecutor(
        32, 32,
        60, TimeUnit.SECONDS,
        new LinkedBlockingQueue<>(102400),
        ThreadFactories.create("JSF-REFER"),
        new ThreadPoolExecutor.CallerRunsPolicy());

// inside scanConsumer
Future
referFuture = CONSUMER_REFER.submit(() -> {
    // original refer logic
});
REFER_FUTURE.add(referFuture);

7. Modified ConsumerBean (DelayConsumerBean) – returns a custom proxy immediately and performs the real refer() asynchronously:

public class DelayConsumerBean
extends ConsumerConfig
implements InitializingBean, FactoryBean
, ApplicationContextAware {
    private static final ThreadPoolExecutor CONSUMER_REFER_EXECUTOR = new ThreadPoolExecutor(
            32, 32, 60, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(102400), ThreadFactories.create("JSF-REFER-2"),
            new ThreadPoolExecutor.CallerRunsPolicy());
    private List
> REFER_FUTURE_LIST = new ArrayList<>();
    @Override
    public T getObject() throws Exception {
        T proxy = (T) Proxy.newProxyInstance(..., new DelayConsumerInvocationHandler());
        REFER_FUTURE_LIST.add(CONSUMER_REFER_EXECUTOR.submit(() -> super.refer()));
        return proxy;
    }
    // invocation handler forwards calls after real consumer is ready
}

8. Tomcat version impact – testing showed that Tomcat 8.5.42 introduced extra per‑bean overhead compared with 8.0.53, slowing the overall startup.

9. Hardware upgrade – migrating the service to newer, higher‑performance machines contributed an additional ~20% speed gain.

Result : after applying the above optimizations—disabling TLD scanning, async HBase warm‑up, async JSF consumer creation, custom BeanPostProcessors, Tomcat version tuning, and hardware migration—the application’s startup time improved by roughly 60%, dramatically shortening deployment cycles for the 11.11 promotion.

backendJavaPerformanceSpringBootasyncBeanPostProcessor
JD Tech
Written by

JD Tech

Official JD technology sharing platform. All the cutting‑edge JD tech, innovative insights, and open‑source solutions you’re looking for, all in one place.

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.