Backend Development 8 min read

Quickly Set Up a Spring MVC Environment Using Annotations

This tutorial walks through building a Spring MVC environment from scratch with Spring 5.3.26, covering traditional XML configuration, servlet registration via web.xml, annotations, and SPI, and shows how to configure a REST controller and required message converters.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Quickly Set Up a Spring MVC Environment Using Annotations

Traditional Spring MVC Configuration

DispatcherServlet requires a WebApplicationContext for its own configuration. A root context can be shared among multiple DispatcherServlet instances, while each servlet can have its own child context that inherits beans from the parent.

The root context typically holds infrastructure beans such as shared repositories and services, which child contexts can override for servlet‑specific beans.

web.xml configuration example:

<code>&lt;web-app&gt;
    &lt;listener&gt;
        &lt;listener-class&gt;org.springframework.web.context.ContextLoaderListener&lt;/listener-class&gt;
    &lt;/listener&gt;
    &lt;context-param&gt;
        &lt;param-name&gt;contextConfigLocation&lt;/param-name&gt;
        &lt;param-value&gt;/WEB-INF/root-context.xml&lt;/param-value&gt;
    &lt;/context-param&gt;
    &lt;servlet&gt;
        &lt;servlet-name&gt;app1&lt;/servlet-name&gt;
        &lt;servlet-class&gt;org.springframework.web.servlet.DispatcherServlet&lt;/servlet-class&gt;
        &lt;init-param&gt;
            &lt;param-name&gt;contextConfigLocation&lt;/param-name&gt;
            &lt;param-value&gt;/WEB-INF/app1-context.xml&lt;/param-value&gt;
        &lt;/init-param&gt;
        &lt;load-on-startup&gt;1&lt;/load-on-startup&gt;
    &lt;/servlet&gt;
    &lt;servlet-mapping&gt;
        &lt;servlet-name&gt;app1&lt;/servlet-name&gt;
        &lt;url-pattern&gt;/app1/*&lt;/url-pattern&gt;
    &lt;/servlet-mapping&gt;
&lt;/web-app&gt;</code>

ContextLoaderListener creates the root container for shared beans (DAO, Service, etc.). DispatcherServlet creates a child container for controller beans and looks up shared beans from the parent.

<code>public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
    protected WebApplicationContext initWebApplicationContext() {
        // Get parent context created by ContextLoaderListener
        WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
        WebApplicationContext wac = null;
        if (this.webApplicationContext != null) {
            wac = this.webApplicationContext;
            if (wac instanceof ConfigurableWebApplicationContext) {
                ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
                if (!cwac.isActive()) {
                    if (cwac.getParent() == null) {
                        cwac.setParent(rootContext);
                    }
                    configureAndRefreshWebApplicationContext(cwac);
                }
            }
        }
        if (wac == null) {
            // Create child context and set parent
            wac = createWebApplicationContext(rootContext);
        }
        return wac;
    }
}</code>

Servlet Registration Methods

Three ways to register a servlet:

1. web.xml registration

<code>&lt;servlet&gt;
    &lt;servlet-name&gt;DemoServlet&lt;/servlet-name&gt;
    &lt;servlet-class&gt;com.pack.servlet.DemoServlet&lt;/servlet-class&gt;
&lt;/servlet&gt;
&lt;servlet-mapping&gt;
    &lt;servlet-name&gt;DemoServlet&lt;/servlet-name&gt;
    &lt;url-pattern&gt;/demo&lt;/url-pattern&gt;
&lt;/servlet-mapping&gt;</code>

2. Annotation‑based registration

<code>@WebServlet(name = "demoServlet", urlPatterns = "/demo")
public class DemoServlet extends HttpServlet { }

@WebServlet(value = {"/demo","/demo1"})
public class DemoServlet extends HttpServlet { }</code>

3. SPI registration (Servlet 3.0+)

Create META-INF/services/javax.servlet.ServletContainerInitializer and implement a custom initializer:

<code>@HandlesTypes({CustomHandler.class})
public class CustomContainerInitializer implements ServletContainerInitializer {
    @Override
    public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {
        if (set != null && !set.isEmpty()) {
            set.forEach(cls -> {
                try {
                    CustomHandler o = (CustomHandler) cls.newInstance();
                    o.onStartup();
                } catch (Exception e) { e.printStackTrace(); }
            });
        }
        ServletRegistration.Dynamic userServlet = servletContext.addServlet("DemoServlet", DemoServlet.class);
        userServlet.addMapping("/demo");
    }
}</code>

Spring MVC Annotation Configuration

Spring provides SpringServletContainerInitializer that detects implementations of WebApplicationInitializer . By extending AbstractAnnotationConfigDispatcherServletInitializer , we can configure the MVC environment without XML.

<code>public class SpringMVCConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[] {RootConfig.class};
    }
    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[] {WebConfig.class};
    }
    @Override
    protected String[] getServletMappings() {
        return new String[] {"/"};
    }
}</code>

Root configuration class:

<code>@Configuration
public class RootConfig { }
</code>

Web configuration class with component scanning:

<code>@Configuration
@ComponentScan(basePackages = {"com.pack.controller"})
public class WebConfig { }
</code>

Sample REST controller:

<code>@RestController
@RequestMapping("/demo")
public class DemoController {
    @GetMapping("")
    public Object index() {
        Map<String, Object> result = new HashMap<>();
        result.put("code", 0);
        result.put("data", "你好");
        return result;
    }
}
</code>

When accessing the controller, a HttpMediaTypeNotAcceptableException may occur because the default RequestMappingHandlerAdapter lacks a suitable message converter.

<code>@Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
    RequestMappingHandlerAdapter adapter = new RequestMappingHandlerAdapter();
    adapter.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
    return adapter;
}
</code>

After adding the converter, the endpoint returns JSON correctly.

Setup complete!

Javaweb developmentServletSpring MVCAnnotation Configuration
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

login 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.