Skip to main content

The Spring Ecosystem & Server Choices

Summary
  • Spring = a dependency injection / framework toolkit. Knows nothing about HTTP.
  • Spring MVC = web framework built on the Servlet API. Sits on Tomcat (or Jetty / Undertow).
  • Spring WebFlux = reactive web framework built on Reactor. Sits on Netty (usually).
  • Spring Boot = an opinionated wrapper that auto-configures everything and gives you a JAR you can just run.
  • Spring Boot is NOT web-only — there are starters for TCP, WebSocket, gRPC, messaging, batch jobs, and more.
Prerequisites

7. Tomcat vs Netty (everything else helps too)

Framework, library, engine — sorting out the words

These words get thrown around interchangeably. They're not the same thing:

TermWhat it isExamples
LibraryCode you callJackson (JSON), Apache HttpClient
FrameworkCode that calls you (inversion of control)Spring, Spring MVC, JUnit
EngineThe runtime that hosts/executes thingsTomcat, Netty, JVM

"Library: you're in charge. Framework: it's in charge. Engine: it runs."

Spring is a framework. Tomcat and Netty are engines. Spring MVC is a framework that runs on top of either of them.

The Spring stack, layer by layer

Reading bottom-up:

  1. Tomcat / Netty — the actual network listener (from doc 7).
  2. Servlet API / Reactor — the standard Java interface (servlet) or reactive API the engine exposes.
  3. Spring Core — provides the DI container that holds your beans.
  4. Spring MVC / WebFlux — the web framework. Maps URLs to controllers, handles JSON, builds responses.
  5. Spring Boot — wires everything together with sensible defaults so you don't have to.
  6. Your code@RestController, @Service, etc.

Spring Core: not about HTTP at all

The first thing to internalise: Spring by itself has nothing to do with web servers. Spring is a dependency injection container + a bag of utilities (AOP, configuration, transaction management, etc.).

@Service
class OrderService {
private final PaymentClient payments;
OrderService(PaymentClient payments) { // Spring injects this
this.payments = payments;
}
}

You can use Spring for:

  • A CLI tool
  • A batch job
  • A desktop app
  • A library
  • ...and oh, also web servers (via Spring MVC or WebFlux)

When people say Spring, they almost always mean Spring + Spring MVC + Spring Boot, but those are layered choices.

Spring MVC: the servlet-based web framework

Spring MVC sits on top of the Servlet API. Under the hood:

  • DispatcherServlet is the single servlet Spring registers with the container. Every request goes through it.
  • It looks up the right @Controller method, invokes it, takes the return value, serializes it (with Jackson), and writes the response.
  • The whole thing runs on a Tomcat worker thread — blocking model.

If you've ever written a @RestController, you've used Spring MVC.

What's NOT Spring MVC vs what IS

Yes (Spring MVC)No (something else)
@RestController, @RequestMapping@Service, @Component (those are Spring Core)
Request mapping, content negotiationBean wiring (Spring Core)
JSON serialization via JacksonJPA/Hibernate (separate Spring Data project)
Filters, interceptorsLogging, scheduling (Spring Core utilities)

Spring WebFlux: the reactive web framework

Spring WebFlux is the alternative to Spring MVC. Same conceptual job (handle HTTP), completely different implementation:

Spring MVCSpring WebFlux
API styleImperative, blockingReactive (Mono<T>, Flux<T>)
Sits onServlet APIProject Reactor
Default engineTomcatReactor Netty
ThreadingThread-per-requestEvent loop
Controller return typeUserMono<User>
DB driverJDBC (blocking)R2DBC (reactive)

A WebFlux controller looks like:

@GetMapping("/orders/{id}")
public Mono<Order> get(@PathVariable Long id) {
return orderRepository.findById(id); // returns Mono<Order>
}

The whole chain is non-blocking — Netty event loop reads the request, Reactor schedules the work, R2DBC sends the query without blocking, the response flows back through the pipeline.

When to pick MVC vs WebFlux

Default to MVC. Reach for WebFlux only when:

  • You have many thousands of concurrent connections (WebSocket, SSE, long-poll)
  • You're building a proxy or gateway that mostly forwards bytes
  • You need streaming semantics with backpressure
  • Your whole downstream stack is already reactive

Don't pick WebFlux just because reactive sounds faster. For typical REST APIs, MVC with virtual threads (doc 7) gives you 95% of the benefit with 5% of the complexity.

Spring Boot: the opinionated wrapper

Spring Boot does not add new web capabilities. It adds:

  1. Starter dependencies — one Maven/Gradle line pulls in everything you need (spring-boot-starter-web = Spring MVC + Tomcat + Jackson + validation + etc.).
  2. Auto-configuration — sees what's on the classpath, configures sensible beans automatically. No XML, minimal Java config.
  3. Embedded engines — Tomcat or Netty is embedded in your JAR. No external Tomcat install; just java -jar app.jar.
  4. Actuator — health checks, metrics, info endpoints out of the box.
  5. Property bindingapplication.yml → typed config classes.

Before Spring Boot, building a Spring web app involved a web.xml, an external Tomcat install, dozens of XML beans, and a deploy step. Now it's @SpringBootApplication + java -jar.

Spring Boot is NOT web-only

This is the headline answer to one of the original questions. Spring Boot has starter modules for many server types beyond plain HTTP:

StarterUnderlying engineWhat you get
spring-boot-starter-webTomcat (servlet)HTTP / REST with Spring MVC
spring-boot-starter-webfluxReactor NettyReactive HTTP with WebFlux
spring-boot-starter-websocketTomcat (servlet)WebSocket via STOMP or raw
spring-boot-starter-integration + Spring Integration TCPNetty under the hoodRaw TCP server (custom protocols)
spring-boot-starter-data-redisLettuce / Jedis (Netty)Redis client (not a server, but uses non-blocking I/O)
spring-boot-starter-amqpRabbitMQ clientMessage consumer/producer
spring-boot-starter-kafkaKafka clientStream processing
spring-boot-starter-batchnone (just JVM)Scheduled batch jobs
spring-grpc-starter (community)gRPC-Java (Netty)gRPC server

You can mix several. A typical Spring Boot service might:

All in one runnable JAR, all using Spring DI, all sharing the same configuration.

Concrete examples for each server type

A REST API server

pom.xml:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
@SpringBootApplication
@RestController
public class App {
@GetMapping("/hello")
String hello() { return "world"; }
public static void main(String[] args) { SpringApplication.run(App.class, args); }
}

Engine: Tomcat (embedded). Concurrency: thread pool.

A reactive HTTP server

pom.xml:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
@RestController
public class HelloController {
@GetMapping("/hello")
Mono<String> hello() { return Mono.just("world"); }
}

Engine: Reactor Netty (embedded). Concurrency: event loop.

A WebSocket server

pom.xml:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
@Configuration
@EnableWebSocket
public class WsConfig implements WebSocketConfigurer {
public void registerWebSocketHandlers(WebSocketHandlerRegistry r) {
r.addHandler(new MyHandler(), "/ws");
}
}

Engine: Tomcat (servlet-based). For 50k+ connections, switch to WebFlux + Reactor Netty.

A raw TCP server (for IoT / hardware)

pom.xml:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-integration</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-ip</artifactId>
</dependency>
@Bean
TcpNetServerConnectionFactory serverFactory() {
return new TcpNetServerConnectionFactory(5000);
}

@Bean
TcpInboundGateway gateway(TcpNetServerConnectionFactory cf) {
TcpInboundGateway g = new TcpInboundGateway();
g.setConnectionFactory(cf);
g.setRequestChannelName("tcpInbound");
return g;
}

Engine: Netty under the hood (via Spring Integration TCP). You can also drop down to raw Netty inside a Spring Boot app for more control.

The matrix: choosing the right server type

I want to build...StarterUnderlying engineConcurrency
REST API (standard)starter-webTomcatThread pool
REST API (high concurrency)starter-web + virtual threadsTomcat (Loom)Virtual threads
Reactive REST / streamingstarter-webfluxReactor NettyEvent loop
WebSocket (small scale)starter-websocketTomcatThread pool
WebSocket (large scale)starter-webfluxReactor NettyEvent loop
gRPC servergrpc-spring-boot-starterNettyEvent loop
Raw TCP / IoTstarter-integration + spring-integration-ipNettyEvent loop
Message consumerstarter-amqp / starter-kafkaBroker client libBackground threads
Scheduled jobsstarter-batch or @ScheduledNone (just JVM)Thread pool

Common confusions

Is Spring Boot a server? No. Spring Boot is the wrapper. The actual server is the embedded engine (Tomcat or Netty) that Spring Boot configures and starts.

Can I run Spring MVC without Spring Boot? Yes — that's how Spring worked before 2014. You'd package a WAR file and deploy it to a standalone Tomcat. Spring Boot just bundles the engine into the JAR.

Does WebFlux replace MVC? No, they're alternatives. Pick one per service. Most services should still use MVC.

Can I mix MVC and WebFlux in one app? Not on the same server (one or the other handles HTTP). But you can use WebClient (the reactive HTTP client from WebFlux) inside an MVC app for outbound calls.

Is Tomcat slower than Netty for the same Spring MVC code? The MVC code is blocking either way, so swapping engines doesn't change much. The benefit of Netty only shows up when the whole stack (Spring WebFlux, R2DBC, etc.) is non-blocking.

What about Quarkus, Micronaut, Helidon? These are alternatives to Spring Boot — same idea (JAR with embedded server), often faster startup, smaller memory. They have similar starter ecosystems.

Where to go next

You now have the full mental model. Next steps depend on what you're building:

  • Building a standard REST service? Start with spring-boot-starter-web. Read the Spring Boot docs for actuator, profiles, configuration.
  • Need real-time features? Read Spring WebSocket docs and the WebFlux reference.
  • Integrating with hardware? Look at Spring Integration TCP or read Netty's user guide for direct use.
  • Operating any Spring Boot service? Learn about Actuator endpoints, logging configuration, and graceful shutdown.

If anything in this series was unclear, file an issue or a TIL — that's how this knowledgebase grows.


← Previous 7. Tomcat vs Netty: Two Concurrency Models

🏁 You've reached the end of the Server Architecture series.