skip to content

How does Spring Boot register and configure the DispatcherServlet? Walk through DispatcherServletAutoConfiguration.

level: principalimportance: should knowfreq 40%

answer

  1. No web.xml — programmatic registration
  2. servlet bean + DispatcherServletRegistrationBean
  3. mapped to spring.mvc.servlet.path (default '/')
  4. single Boot context, no root/child split
  5. @ConditionalOnMissingBean = override-able

basics

~10 s

Spring Boot's DispatcherServletAutoConfiguration creates the DispatcherServlet bean and a DispatcherServletRegistrationBean that registers it with the embedded servlet container, mapped to spring.mvc.servlet.path (default '/'), using the single Boot application context.

solid answer

~40 s

In Spring Boot there's no web.xml. `DispatcherServletAutoConfiguration` (in `spring-boot-autoconfigure`, gated on a servlet web app with `DispatcherServlet` on the classpath) provides two nested configs: `DispatcherServletConfiguration` creates the `DispatcherServlet` bean (named `dispatcherServlet`), binding `spring.mvc` / `server` properties like `throwExceptionIfNoHandlerFound`; and `DispatcherServletRegistrationConfiguration` creates a `DispatcherServletRegistrationBean` that registers the servlet with the embedded container (Tomcat/Jetty/Undertow) and maps it to `spring.mvc.servlet.path` (default `'/'`). Because Boot uses a **single** application context, the DispatcherServlet uses that context directly rather than creating a child — the classic root/servlet split is gone. Registration goes through Boot's `ServletContextInitializer`/`RegistrationBean` mechanism against the programmatic `ServletContext`, not `ServletConfig` XML. `@ConditionalOnMissingBean` lets you override either bean. Multipart is wired by a separate autoconfiguration.

code

java · 40 lines
java
// Shape of Spring Boot's auto-config (abridged, illustrative)
@AutoConfiguration(after = ServletWebServerFactoryAutoConfiguration.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
public class DispatcherServletAutoConfiguration {

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass(ServletRegistration.class)
    protected static class DispatcherServletConfiguration {
        @Bean(name = "dispatcherServlet")
        @ConditionalOnMissingBean
        public DispatcherServlet dispatcherServlet(WebMvcProperties props) {
            DispatcherServlet d = new DispatcherServlet();
            d.setThrowExceptionIfNoHandlerFound(props.isThrowExceptionIfNoHandlerFound());
            d.setDispatchOptionsRequest(props.isDispatchOptionsRequest());
            return d;
        }
    }

    @Configuration(proxyBeanMethods = false)
    protected static class DispatcherServletRegistrationConfiguration {
        @Bean(name = "dispatcherServletRegistration")
        @ConditionalOnMissingBean(value = DispatcherServlet.class,
                name = "dispatcherServletRegistration")
        public DispatcherServletRegistrationBean dispatcherServletRegistration(
                DispatcherServlet dispatcherServlet, WebMvcProperties props) {
            return new DispatcherServletRegistrationBean(
                dispatcherServlet, props.getServlet().getPath()); // default "/"
        }
    }
}

// Add a SECOND dispatcher for, say, an isolated API:
@Bean
public ServletRegistrationBean<DispatcherServlet> apiDispatcher(DispatcherServlet ds) {
    ServletRegistrationBean<DispatcherServlet> reg =
        new ServletRegistrationBean<>(ds, "/api2/*");
    reg.setName("apiDispatcher");
    return reg;
}

go deeper

for a junior

Know Boot auto-registers the DispatcherServlet mapped to '/'.

for a middle

Know the servlet bean + registration bean and the spring.mvc.servlet.path property.

for a senior

Explain programmatic RegistrationBean registration and @ConditionalOnMissingBean override points.

for a principal

Discuss the single-context consequence, multiple-dispatcher setups, ordering, and no-handler/error-path implications.

## The problem Boot solves Classic MVC registers the DispatcherServlet in `web.xml` or a `WebApplicationInitializer`, and builds a root+servlet context hierarchy. Spring Boot runs an **embedded** servlet container and has no `web.xml`, so it must register the DispatcherServlet **programmatically** and configure it from properties. ## DispatcherServletAutoConfiguration Lives in `org.springframework.boot.autoconfigure.web.servlet`. Key conditions/ordering: - `@AutoConfiguration(after = ServletWebServerFactoryAutoConfiguration.class)` — the web server factory must be configured first. - `@ConditionalOnWebApplication(type = SERVLET)` and `@ConditionalOnClass(DispatcherServlet.class)`. It declares two nested `@Configuration` classes: ### 1. DispatcherServletConfiguration — the servlet bean - `@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)` → `dispatcherServlet` creates a `DispatcherServlet`. - It copies bound properties: from `WebMvcProperties` it sets things like `setDispatchOptionsRequest`, `setDispatchTraceRequest`, `setThrowExceptionIfNoHandlerFound`, `setPublishEvents`, `setEnableLoggingRequestDetails`. - Guarded by `@ConditionalOnMissingBean` (matched by the well-known name) so you can define your own. - Also conditionally exposes a `MultipartResolver` bean renaming fix (`multipartResolver`) if a bean of another name exists. ### 2. DispatcherServletRegistrationConfiguration — the registration - `@Bean` → `DispatcherServletRegistrationBean` (a `ServletRegistrationBean` subclass). - It maps the servlet to `webMvcProperties.getServlet().getPath()` — property `spring.mvc.servlet.path`, default `'/'`. - `@ConditionalOnMissingBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)` — override-able. - The registration bean implements `ServletContextInitializer`; at container startup Boot calls `onStartup(ServletContext)` which does `servletContext.addServlet(...).addMapping(...)`. This is the programmatic equivalent of the old `web.xml` `<servlet-mapping>`. ## Single context, no hierarchy Crucially, the Boot-created DispatcherServlet is given the **existing** application context (Boot sets it, and `FrameworkServlet` won't create a child because a context is already supplied). So there's **one** context — root and servlet beans coexist. This is why Boot apps don't hit the classic duplicate-scan / cross-context AOP problems. ## ServletConfig vs. programmatic registration In a servlet container, a servlet receives a `ServletConfig` (init params, servlet name, ServletContext). In XML you'd set `contextConfigLocation` init-params. In Boot, the `RegistrationBean` supplies init params programmatically via `setInitParameters(...)`, and the servlet name/mapping via the registration bean. You rarely touch `ServletConfig` directly. ## Customization hooks - Change the mapping/path: `spring.mvc.servlet.path=/app` (note: this becomes a servlet path prefix, affects `@RequestMapping` base and `ServletContext` path handling). - `spring.mvc.throw-exception-if-no-handler-found`, `spring.mvc.dispatch-options-request`, etc. - Register a **second** DispatcherServlet by defining your own `ServletRegistrationBean<DispatcherServlet>` with a distinct mapping (e.g. a separate API context). - Override the servlet entirely with your own `@Bean DispatcherServlet` (thanks to `@ConditionalOnMissingBean`). ## Gotchas - Setting `spring.mvc.servlet.path` to something other than `/` changes how static resources and the error path resolve; the default `/` maps everything. - The registration bean's **order** matters if you register multiple servlets/filters. - `throwExceptionIfNoHandlerFound` behavior: in recent Boot versions no-handler results in a `NoHandlerFoundException` funneled to the error handling, but historically returning a container 404 that bypassed `@ControllerAdvice` was a common surprise. - Because there's a single context, an errant `@ComponentScan` can no longer 'hide' web beans from services — but it also means everything shares one lifecycle.

  • Why does a Spring Boot app not suffer the classic duplicate-component-scan problem?
    Because Boot uses a single application context and hands it to the DispatcherServlet instead of creating a child servlet context — there's no second context to re-scan into.
  • How would you change the DispatcherServlet's URL mapping in Boot?
    Set spring.mvc.servlet.path (default '/'). It's consumed by DispatcherServletRegistrationBean to set the servlet mapping; be aware it also shifts static-resource and error path resolution.
  • How can you fully replace Boot's DispatcherServlet?
    Define your own @Bean named dispatcherServlet (and/or the registration bean). Both are guarded by @ConditionalOnMissingBean, so your bean wins and auto-config backs off.
  • What replaces web.xml servlet registration in Boot?
    The ServletContextInitializer / RegistrationBean mechanism: DispatcherServletRegistrationBean.onStartup calls servletContext.addServlet(...).addMapping(...) programmatically at embedded-container startup.

saying these in an interview costs you the question

  • Saying Boot uses web.xml to register the DispatcherServlet
  • Claiming Boot still builds a separate root and servlet context by default
  • Thinking you can't override the DispatcherServlet bean
  • Confusing spring.mvc.servlet.path with server.servlet.context-path

context