How Spring Boot Boots Embedded Tomcat: A Deep Dive into the Startup Process

This article explains how Spring Boot launches its embedded Tomcat server, covering Maven dependencies, the main application class, production packaging, the SpringApplication.run workflow, context creation, refresh mechanics, TomcatWebServer initialization, and the internal Tomcat component hierarchy.

Senior Brother's Insights
Senior Brother's Insights
Senior Brother's Insights
How Spring Boot Boots Embedded Tomcat: A Deep Dive into the Startup Process

Embedded Tomcat in Spring Boot

Spring Boot simplifies development by providing an embedded Tomcat server that starts with a single command, eliminating the need for external XML configuration or manual Tomcat setup.

Maven Dependency

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>2.1.6.RELEASE</version>
</dependency>

Main Application Class

@SpringBootApplication
public class MySpringbootTomcatStarter {
    public static void main(String[] args) {
        Long time = System.currentTimeMillis();
        SpringApplication.run(MySpringbootTomcatStarter.class);
        System.out.println("===应用启动耗时:" + (System.currentTimeMillis() - time) + "===");
    }
}

The @SpringBootApplication annotation and SpringApplication.run() are the two key statements that launch the application.

Packaging for Production

When deploying to an external Tomcat, the embedded Tomcat is excluded and the project is packaged as a WAR. The relevant Maven configuration removes the embedded Tomcat starter and adds the javax.servlet-api dependency with provided scope.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<!-- Add servlet API dependency -->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
    <scope>provided</scope>
</dependency>

The main class now extends SpringBootServletInitializer and overrides configure() to return the application sources.

@SpringBootApplication
public class MySpringbootTomcatStarter extends SpringBootServletInitializer {
    public static void main(String[] args) {
        Long time = System.currentTimeMillis();
        SpringApplication.run(MySpringbootTomcatStarter.class);
        System.out.println("===应用启动耗时:" + (System.currentTimeMillis() - time) + "===");
    }

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        return builder.sources(this.getClass());
    }
}

SpringApplication.run Details

The run method ultimately creates a SpringApplication instance and calls its run method, which prepares the environment, creates the application context, and refreshes it.

public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
    return run(new Class[]{primarySource}, args);
}

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

Creating the Application Context

protected ConfigurableApplicationContext createApplicationContext() {
    Class<?> contextClass = this.applicationContextClass;
    if (contextClass == null) {
        switch (this.webApplicationType) {
            case SERVLET:
                contextClass = Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");
                break;
            case REACTIVE:
                contextClass = Class.forName("org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext");
                break;
            default:
                contextClass = Class.forName("org.springframework.context.annotation.AnnotationConfigApplicationContext");
        }
    }
    return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}

For a servlet‑based application, Spring Boot creates an AnnotationConfigServletWebServerApplicationContext, which extends ServletWebServerApplicationContext and ultimately AbstractApplicationContext.

Refreshing the Context

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

protected void refresh(ApplicationContext applicationContext) {
    ((AbstractApplicationContext) applicationContext).refresh();
}

The AbstractApplicationContext.refresh() method prepares the bean factory, invokes post‑processors, registers bean post‑processors, initializes the message source and event multicaster, calls onRefresh(), registers listeners, finishes bean factory initialization, and finally publishes the refreshed event.

onRefresh and Web Server Creation

protected void onRefresh() {
    super.onRefresh();
    try {
        this.createWebServer();
    } catch (Throwable ex) {
        // handle
    }
}

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

The ServletWebServerFactory interface has four implementations: TomcatServletWebServerFactory, JettyServletWebServerFactory, UndertowServletWebServerFactory, and the abstract base class.

TomcatServletWebServerFactory

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

protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
    return new TomcatWebServer(tomcat, getPort() >= 0);
}

TomcatWebServer Initialization

public TomcatWebServer(Tomcat tomcat, boolean autoStart) {
    Assert.notNull(tomcat, "Tomcat Server must not be null");
    this.tomcat = tomcat;
    this.autoStart = autoStart;
    initialize();
}

private void initialize() throws WebServerException {
    logger.info("Tomcat initialized with port(s): " + getPortsDescription(false));
    synchronized (this.monitor) {
        try {
            addInstanceIdToEngineName();
            Context context = findContext();
            context.addLifecycleListener(event -> {
                if (context.equals(event.getSource()) && Lifecycle.START_EVENT.equals(event.getType())) {
                    removeServiceConnectors();
                }
            });
            this.tomcat.start();
            rethrowDeferredStartupExceptions();
            // bind JNDI class loader
            startDaemonAwaitThread();
        } catch (Exception ex) {
            stopSilently();
            destroySilently();
            throw new WebServerException("Unable to start embedded Tomcat", ex);
        }
    }
}

@Override
public void start() throws WebServerException {
    synchronized (this.monitor) {
        if (this.started) return;
        try {
            addPreviouslyRemovedConnectors();
            Connector connector = this.tomcat.getConnector();
            if (connector != null && this.autoStart) {
                performDeferredLoadOnStartup();
            }
            checkThatConnectorsHaveStarted();
            this.started = true;
            logger.info("Tomcat started on port(s): " + getPortsDescription(true) + " with context path '" + getContextPath() + "'");
        } catch (ConnectorStartFailedException ex) {
            stopSilently();
            throw ex;
        } catch (Exception ex) {
            throw new WebServerException("Unable to start embedded Tomcat server", ex);
        } finally {
            Context context = findContext();
            ContextBindings.unbindClassLoader(context, context.getNamingToken(), getClass().getClassLoader());
        }
    }
}

@Override
public void stop() throws WebServerException {
    synchronized (this.monitor) {
        boolean wasStarted = this.started;
        try {
            this.started = false;
            try {
                stopTomcat();
                this.tomcat.destroy();
            } catch (LifecycleException ignored) {}
        } catch (Exception ex) {
            throw new WebServerException("Unable to stop embedded Tomcat", ex);
        } finally {
            if (wasStarted) {
                containerCounter.decrementAndGet();
            }
        }
    }
}

Tomcat Internal Structure

Tomcat top‑level structure diagram
Tomcat top‑level structure diagram

A Tomcat Server contains multiple Service objects. Each Service has one Container (the Engine) and one or more Connector instances that handle network connections.

Engine > Host > Context > Wrapper hierarchy
Engine > Host > Context > Wrapper hierarchy

The Engine contains Hosts; each Host contains Contexts; each Context contains Wrappers that manage individual Servlets.

Summary

Spring Boot starts by instantiating SpringApplication, which configures properties, prepares the environment, creates an appropriate application context, and refreshes it. The refresh step triggers the creation of an embedded Tomcat via TomcatServletWebServerFactory. Tomcat itself is built from a Server containing Services, Connectors, and a hierarchy of Containers (Engine → Host → Context → Wrapper). The embedded server is started, bound to the JVM class loader, and runs until the application shuts down.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

spring-bootembedded server
Senior Brother's Insights
Written by

Senior Brother's Insights

A public account focused on workplace, career growth, team management, and self-improvement. The author is the writer of books including 'SpringBoot Technology Insider' and 'Drools 8 Rule Engine: Core Technology and Practice'.

0 followers
Reader feedback

How this landed with the community

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.