Backend Development 25 min read

Deep Dive into SpringBoot Startup, Auto‑Configuration, Event‑Driven Architecture, and Conditional Annotations

This article provides an in‑depth analysis of SpringBoot’s startup process, automatic configuration mechanism, event‑driven architecture, and conditional annotation handling, illustrating key source code snippets and explaining how the framework loads and initializes beans, listeners, and embedded Tomcat server.

Top Architect
Top Architect
Top Architect
Deep Dive into SpringBoot Startup, Auto‑Configuration, Event‑Driven Architecture, and Conditional Annotations

Introduction

The article explains how SpringBoot, as a wrapper around Spring, offers an "out‑of‑the‑box" experience with low entry cost, but its internal implementation requires understanding the core Spring principles. It sets the stage for a detailed examination of the startup flow, auto‑configuration, event publishing, and conditional annotations.

Startup Principle

SpringBoot can be launched in three common ways: @SpringBootApplication public class SpringbootDemo { public static void main(String[] args) { SpringApplication.run(SpringbootDemo.class, args); } } The run method creates an ApplicationContext , prepares the environment, loads listeners, and finally refreshes the context.

run Method Overview

The core run method performs timing, environment preparation, listener invocation, context creation, and exception handling. It ultimately calls refreshContext to start the Spring container.

public ConfigurableApplicationContext run(String... args) {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    // ... load listeners, environment, banner, etc.
    context = createApplicationContext();
    refreshContext(context);
    // ... log startup info, invoke runners
    return context;
}

ApplicationContext Creation

createApplicationContext selects the appropriate context class based on the web application type (Servlet, Reactive, or default) and instantiates it via reflection.

protected ConfigurableApplicationContext createApplicationContext() {
    Class
contextClass = this.applicationContextClass;
    if (contextClass == null) {
        switch (this.webApplicationType) {
            case SERVLET:
                contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
                break;
            case REACTIVE:
                contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
                break;
            default:
                contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
        }
    }
    return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}

Refresh Process

The refreshContext method delegates to the parent refresh implementation, which prepares the bean factory, registers post‑processors, initializes the message source, event multicaster, and finally instantiates all non‑lazy singletons.

private void refreshContext(ConfigurableApplicationContext context) {
    refresh(context);
    if (this.registerShutdownHook) {
        try { context.registerShutdownHook(); } catch (AccessControlException ex) { }
    }
}

Embedded Tomcat Startup

During onRefresh , SpringBoot creates a TomcatServletWebServerFactory and starts Tomcat via createWebServer . The factory configures the connector, host, and context before returning a TomcatWebServer instance.

public WebServer getWebServer(ServletContextInitializer... initializers) {
    Tomcat tomcat = new Tomcat();
    File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
    tomcat.setBaseDir(baseDir.getAbsolutePath());
    Connector connector = new Connector(this.protocol);
    tomcat.getService().addConnector(connector);
    // ... customize connector, add additional connectors
    prepareContext(tomcat.getHost(), initializers);
    return getTomcatWebServer(tomcat);
}

Event‑Driven Architecture

SpringBoot publishes a series of events throughout the container lifecycle (e.g., ApplicationStartingEvent , ApplicationReadyEvent , ContextClosedEvent ). Listeners are registered via spring.factories and include logging, configuration, and cache‑clearing listeners.

Auto‑Configuration Mechanism

The heart of SpringBoot’s auto‑configuration lies in @EnableAutoConfiguration , which imports AutoConfigurationImportSelector . This selector reads META-INF/spring.factories to obtain all EnableAutoConfiguration entries, filters them, and registers the resulting configuration classes.

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class })
public @interface SpringBootApplication {}

The selector implements DeferredImportSelector to allow ordering and conditional loading. It ultimately creates an AutoConfigurationEntry containing the selected configuration class names.

Processing @Import and @ImportSelector

The ConfigurationClassParser handles @Import by invoking ImportSelector or ImportBeanDefinitionRegistrar . Deferred selectors are collected and processed after all regular imports, ensuring proper ordering.

Conditional Annotations

Annotations such as @ConditionalOnProperty , @ConditionalOnClass , and @ConditionalOnMissingBean are meta‑annotated with @Conditional . During parsing, ConditionEvaluator.shouldSkip loads each Condition implementation and calls its matches method. If any condition returns false, the associated bean definition is skipped.

public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) {
    if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) {
        return false;
    }
    List
conditions = new ArrayList<>();
    for (String[] conditionClasses : getConditionClasses(metadata)) {
        for (String conditionClass : conditionClasses) {
            Condition condition = getCondition(conditionClass, this.context.getClassLoader());
            conditions.add(condition);
        }
    }
    AnnotationAwareOrderComparator.sort(conditions);
    for (Condition condition : conditions) {
        if (!condition.matches(this.context, metadata)) {
            return true;
        }
    }
    return false;
}

Specific condition implementations (e.g., OnBeanCondition ) examine the bean factory for matching types, names, or annotations, returning a ConditionOutcome that drives the final decision.

Conclusion

The article demystifies SpringBoot’s core startup flow, auto‑configuration loading via SPI, event publishing, and conditional bean registration, equipping readers with the knowledge to extend or customize the framework and to design their own SpringBoot starters.

backendJavaSpringBootstartupAutoConfigurationconditional
Top Architect
Written by

Top Architect

Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.

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.