Backend Development 14 min read

Understanding Spring Boot @SpringBootApplication and Its Auto‑Configuration Mechanism

This article explains how Spring Boot’s @SpringBootApplication annotation combines several meta‑annotations, how auto‑configuration classes are loaded from META‑INF/spring.factories, and demonstrates building a custom starter that configures a Tomcat web server, with full code examples and Maven setup.

Top Architect
Top Architect
Top Architect
Understanding Spring Boot @SpringBootApplication and Its Auto‑Configuration Mechanism

The author begins by showing the main Spring Boot configuration class:

@SpringBootApplication
public class StartEurekaApplication {
    public static void main(String[] args) {
        SpringApplication.run(StartEurekaApplication.class, args);
    }
}

He points out that @SpringBootApplication is a composite annotation that includes @SpringBootConfiguration , @EnableAutoConfiguration and @ComponentScan .

@SpringBootConfiguration is essentially @Configuration , allowing the class to register additional beans without XML:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration { }

@EnableAutoConfiguration activates Spring Boot’s auto‑configuration by importing AutoConfigurationImportSelector and using @AutoConfigurationPackage to scan the package of the main class and its sub‑packages.

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration { }

public interface AutoConfigurationImportSelector extends DeferredImportSelector { }

The AutoConfigurationPackage registers the base package via ImportBeanDefinitionRegistrar :

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        AutoConfigurationPackages.register(registry, (new AutoConfigurationPackages.PackageImport(metadata)).getPackageName());
    }
    public Set
determineImports(AnnotationMetadata metadata) {
        return Collections.singleton(new AutoConfigurationPackages.PackageImport(metadata));
    }
}

Spring Boot then loads all auto‑configuration class names from META-INF/spring.factories using SpringFactoriesLoader and registers them in the application context.

public static List
loadFactoryNames(Class
factoryClass, @Nullable ClassLoader classLoader) {
    String factoryClassName = factoryClass.getName();
    return (List) loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}

private static Map
> loadSpringFactories(@Nullable ClassLoader classLoader) {
    // reads META-INF/spring.factories and builds a multivalued map
}

During the boot process, SpringApplication.run creates the context, prepares the environment, prints the banner, and finally calls refreshContext , which invokes the standard Spring AbstractApplicationContext.refresh() lifecycle (prepareRefresh, obtainFreshBeanFactory, postProcessBeanFactory, invokeBeanFactoryPostProcessors, registerBeanPostProcessors, initMessageSource, initApplicationEventMulticaster, onRefresh, registerListeners, finishBeanFactoryInitialization, finishRefresh).

public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        prepareRefresh();
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        prepareBeanFactory(beanFactory);
        postProcessBeanFactory(beanFactory);
        invokeBeanFactoryPostProcessors(beanFactory);
        registerBeanPostProcessors(beanFactory);
        initMessageSource();
        initApplicationEventMulticaster();
        onRefresh();
        registerListeners();
        finishBeanFactoryInitialization(beanFactory);
        finishRefresh();
    }
}

For web applications, ServletWebServerApplicationContext overrides onRefresh() to create a web server via ServletWebServerFactory . The default implementation uses TomcatServletWebServerFactory to instantiate an embedded Tomcat server.

@Override
protected void onRefresh() {
    super.onRefresh();
    try {
        createWebServer();
    } catch (Throwable ex) {
        throw new ApplicationContextException("Unable to start web server", ex);
    }
}

private void createWebServer() {
    if (webServer == null && servletContext == null) {
        ServletWebServerFactory factory = getWebServerFactory();
        this.webServer = factory.getWebServer(getSelfInitializer());
    } else if (servletContext != null) {
        getSelfInitializer().onStartup(servletContext);
    }
    initPropertySources();
}

public interface ServletWebServerFactory {
    WebServer getWebServer(ServletContextInitializer... initializers);
}

The Tomcat factory configures the connector, base directory, and context before returning a TomcatWebServer instance:

@Override
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);
    customizeConnector(connector);
    tomcat.setConnector(connector);
    tomcat.getHost().setAutoDeploy(false);
    configureEngine(tomcat.getEngine());
    for (Connector additionalConnector : this.additionalTomcatConnectors) {
        tomcat.getService().addConnector(additionalConnector);
    }
    prepareContext(tomcat.getHost(), initializers);
    return getTomcatWebServer(tomcat);
}

Finally, the article shows how to create a custom Spring Boot starter: define Maven coordinates, depend on spring-boot-autoconfigure , write a configuration class annotated with @Configuration , @ConditionalOnClass , @EnableConfigurationProperties , and expose a bean with @ConditionalOnMissingBean . Register the starter in META-INF/spring.factories so that it is auto‑loaded.

org.springframework.boot
spring-boot-starter-data-redis
org.springframework.boot
spring-boot-starter-freemarker
org.springframework.boot
spring-boot-starter-parent
2.1.4.RELEASE
@Configuration
@ConditionalOnClass(GwService.class)
@EnableConfigurationProperties(GwProperties.class)
public class GwAutoConfiguration {
    @Bean
    @ConditionalOnMissingBean
    public GwService gwService() {
        return new GwServiceImpl();
    }
}

# META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.gw.GwAutoConfiguration

In summary, the post walks through the inner workings of @SpringBootApplication , the auto‑configuration loading process, the creation of an embedded Tomcat server, and demonstrates building a simple reusable starter.

BackendJavaSpring BootTomcatAuto‑ConfigurationStarter
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.