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.
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><web-app>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/root-context.xml</param-value>
</context-param>
<servlet>
<servlet-name>app1</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/app1-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>app1</servlet-name>
<url-pattern>/app1/*</url-pattern>
</servlet-mapping>
</web-app></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><servlet>
<servlet-name>DemoServlet</servlet-name>
<servlet-class>com.pack.servlet.DemoServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>DemoServlet</servlet-name>
<url-pattern>/demo</url-pattern>
</servlet-mapping></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!
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.
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.