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.
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
A Tomcat Server contains multiple Service objects. Each Service has one Container (the Engine) and one or more Connector instances that handle network connections.
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.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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'.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
