How does Spring Boot decide whether an app is SERVLET, REACTIVE, or NONE, and how do you override it?
answer
- deduceFromClasspath via ClassUtils.isPresent
- DispatcherServlet -> SERVLET, DispatcherHandler only -> REACTIVE
- both MVC+WebFlux -> SERVLET wins
- NONE = no server, plain context
- override: setWebApplicationType / .web() / spring.main.web-application-type
basics
~20 sBoot inspects the classpath. If Spring MVC (servlet) classes are present it picks SERVLET; if only WebFlux is present it picks REACTIVE; if no web classes are present it picks NONE. Override with setWebApplicationType() or spring.main.web-application-type.
solid answer
~40 sBoot auto-detects the WebApplicationType by scanning the classpath. SERVLET is chosen when servlet + Spring MVC classes (e.g. DispatcherServlet) are present. REACTIVE is chosen when WebFlux (DispatcherHandler) is present but MVC is not. NONE is chosen when no web stack is on the classpath, giving a plain ApplicationContext with no embedded server — ideal for batch/CLI apps. The chosen type determines which ApplicationContext subclass is created and whether an embedded server starts. If both MVC and WebFlux are present, SERVLET wins. You override it programmatically with new SpringApplication(...).setWebApplicationType(WebApplicationType.NONE), via SpringApplicationBuilder.web(...), or with the property spring.main.web-application-type=none|servlet|reactive. A common use: forcing NONE so a web-dependency app runs as a task without binding a port.
code
java · 14 linespublic static void main(String[] args) {
SpringApplication app = new SpringApplication(BatchApp.class);
// Force a non-web app even though web starters are on the classpath
app.setWebApplicationType(WebApplicationType.NONE);
app.run(args);
}
// Equivalent with the builder:
// new SpringApplicationBuilder(BatchApp.class)
// .web(WebApplicationType.NONE)
// .run(args);
// Equivalent via property (application.properties):
// spring.main.web-application-type=nonego deeper
Should know the three types and that detection is classpath-based.
Should explain the deduction order and how to override it (property or setter).
Should explain why SERVLET wins over REACTIVE and the context-class consequences.
Should reason about mixed-stack modularization and forcing NONE for transitive web deps in shared libraries.
## The three types `WebApplicationType` is an enum with three values: - **SERVLET** — a traditional servlet web app (Spring MVC on an embedded servlet container like Tomcat/Jetty/Undertow). Creates an `AnnotationConfigServletWebServerApplicationContext`. - **REACTIVE** — a reactive web app (Spring WebFlux on Netty or a reactive servlet). Creates an `AnnotationConfigReactiveWebServerApplicationContext`. - **NONE** — not a web app: no embedded server, plain `AnnotationConfigApplicationContext`. The JVM will exit once `run()` finishes (unless you keep it alive). Great for batch jobs, CLIs, schedulers. ## How detection works (classpath deduction) `WebApplicationType.deduceFromClasspath()` uses `ClassUtils.isPresent(...)` checks: 1. If `org.springframework.web.reactive.DispatcherHandler` is present **and** neither `org.springframework.web.servlet.DispatcherServlet` nor Jersey's `ServletContainer` is present → **REACTIVE**. 2. Else if the servlet classes `jakarta.servlet.Servlet` and `ConfigurableWebApplicationContext` are **not** all present → **NONE**. 3. Otherwise → **SERVLET**. The practical consequence: **if both `spring-boot-starter-web` (MVC) and `spring-boot-starter-webflux` are on the classpath, the app runs as SERVLET**, because the presence of `DispatcherServlet` disqualifies REACTIVE. This surprises people who add WebFlux to an MVC app expecting reactive behavior. ## Why it matters The type dictates the `ApplicationContext` class, whether an embedded web server is created, and which auto-configurations activate. A NONE app never opens a port — so a service pulled in transitively won't accidentally bind 8080. ## Overriding Several ways, in increasing convenience: - Programmatic on `SpringApplication`: `app.setWebApplicationType(WebApplicationType.NONE);` - Via `SpringApplicationBuilder`: `.web(WebApplicationType.REACTIVE)` - Via property (bound early, before context creation): `spring.main.web-application-type=none` Because the type is decided **before** the context is created and refreshed, the property is read from the `Environment` during that early phase — you can set it in `application.properties`, as a `-D` system property, or a command-line `--spring.main.web-application-type=...`. ## Gotchas - Adding WebFlux to an MVC project does not make it reactive — SERVLET wins. - Forcing NONE on a web app disables the embedded server and web auto-config, so endpoints won't be served. - In tests, `@SpringBootTest(webEnvironment = ...)` controls a related but separate notion (MOCK vs RANDOM_PORT), not `WebApplicationType` directly.
- You add spring-boot-starter-webflux to an existing MVC app — does it become reactive?No. DispatcherServlet is still present, so deduction picks SERVLET; WebFlux beans exist but the app runs on the servlet stack. You'd have to remove MVC or force REACTIVE explicitly.
- What context class is created for each type?SERVLET -> AnnotationConfigServletWebServerApplicationContext, REACTIVE -> AnnotationConfigReactiveWebServerApplicationContext, NONE -> AnnotationConfigApplicationContext.
saying these in an interview costs you the question
- Believing REACTIVE wins when both MVC and WebFlux are present
- Saying detection reads a property first rather than the classpath
- Thinking NONE still starts an embedded server on 8080