Measuring and cutting startup time

Spring Boot · lesson 37 of 39 · 5 min read

Find the slow beans instead of guessing, then decide what is safe to defer.

Open this lesson in the learning hub

Key points

  • Startup cost is dominated by three things: classpath scanning, condition evaluation, and beans that do real work in their constructor or @PostConstruct.
  • Measure before changing anything. BufferingApplicationStartup records every step and the /actuator/startup endpoint reports them sorted by duration.
  • Eagerly connecting to a database, a broker or a remote configuration server during bean creation is usually the single largest item, and it is also what makes startup fail when a dependency is briefly unavailable.
  • spring.main.lazy-initialization=true defers bean creation until first use. It genuinely speeds up local development, but it moves wiring errors from deployment to the first request - a poor trade in production.
  • A better targeted fix is @Lazy on the specific expensive beans, keeping fail-fast behaviour for everything else.
  • For large applications, the spring-context-indexer generates a component index at build time so scanning reads one file instead of walking the classpath.

Example

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(Application.class);
        // Record the startup timeline so /actuator/startup can report it.
        // Bounded: it keeps the most recent N events rather than growing.
        app.setApplicationStartup(new BufferingApplicationStartup(2048));
        app.run(args);
    }
}

/*
 * curl localhost:8080/actuator/startup | jq '.timeline.events
 *      | sort_by(-.duration) | .[0:5]'
 *
 * A typical result - the answer is nearly always one bean, not "Spring is slow":
 *
 *   spring.beans.instantiate  entityManagerFactory   4.10s
 *   spring.beans.instantiate  liquibase              1.80s
 *   spring.beans.instantiate  kafkaAdminClient       0.90s
 *   spring.context.refresh                           0.30s
 *   spring.beans.instantiate  objectMapper           0.02s
 */

// Targeted, rather than making the whole application lazy:
@Component
@Lazy
class ReportGenerator {

    ReportGenerator(TemplateEngine engine) {
        engine.precompileAll();   // 3 seconds, and only needed by one endpoint
    }
}

Measure with /actuator/startup first - it is almost always one eager bean doing I/O, not the framework.

This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the Spring Boot course, and every lesson in it is listed on the Spring Boot contents page.