Comprehensive Practical Tutorial for Spring MVC and RESTful API Development

  1. Add Required Dependencies Include core Spring MVC and dependent JAR files in your project's build path: spring-webmvc, spring-core, spring-context, spring-beans, spring-expression, and commons-logging.

  2. Configure web.xml Add the DispatcherServlet configuration to WEB-INF/web.xml:

<!-- Configure Spring MVC DispatcherServlet and request mapping -->
<servlet>
    <servlet-name>app-dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring-mvc-servlet.xml</param-value>
    </init-param>
    <!-- Load servlet on application startup for faster initial requests -->
    <!-- <load-on-startup>1</load-on-startup> -->
</servlet>

<servlet-mapping>
    <servlet-name>app-dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>
  1. Create Spring MVC Configuration File Create spring-mvc-servlet.xml in src/main/resources:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context 
        https://www.springframework.org/schema/context/spring-context-5.3.xsd
        http://www.springframework.org/schema/mvc 
        https://www.springframework.org/schema/mvc/spring-mvc-5.3.xsd">

    <!-- Scan for controller components in the specified base package -->
    <context:component-scan base-package="com.example.springmvc.controller" />

    <!-- Enable default servlet handling for static resources -->
    <mvc:default-servlet-handler />

    <!-- Enable annotation-driven MVC configuration -->
    <mvc:annotation-driven />
    
    <!-- Configure internal resource view resolver for JSP views -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" 
            id="viewResolver">
        <!-- View file prefix -->
        <property name="prefix" value="/WEB-INF/jsp/" />
        <!-- View file suffix -->
        <property name="suffix" value=".jsp" />
    </bean>
</beans>
  1. Create View Files Create a jsp directory under WEB-INF, then add hello.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Hello World</title>
</head>
<body>
    <h1>Hello World</h1>
</body>
</html>
  1. Create MVC Controller Create HelloMvcController.java in com.example.springmvc.controller:
package com.example.springmvc.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/mvc")
public class HelloMvcController {

    @RequestMapping("/hello")
    public String sayHello() {        
        return "hello";
    }
}
  1. Test the Application Start your servlet container, then access http://localhost:8080/your-project-name/mvc/hello to view the Hello World page.

Configuration Breakdown

  1. DispatcherServlet: The front controller defined in web.xml, which intercepts matching requests and routes them to the appropriate controller. This is the first step in setting up Spring MVC.
  2. InternalResourceViewResolver: Resolves logical view names to actual JSP files using the configured prefix and suffix.
  3. Core Annotations:
    • @Controller: Registers the class as a Spring-managed bean
    • @RequestMapping: Maps HTTP requests to controller methods

Common Spring MVC Annotations

  • @Controller: Registers a class as a Spring controller bean
  • @RequestMapping: Maps specific URL requests to controller methods
  • @RequestBody: Reads the HTTP request body, parses it with HttpMessageConverter, and binds it to method parameters
  • @ResponseBody: Converts the method return value to the specified response format (e.g., JSON) using HttpMessageConverter
  • @ModelAttribute: Populates model data before method execution, or binds request parameters to method parameters
  • @RequestParam: Binds HTTP query parameters to controller method parameters
  • @PathVariable: Binds URL path variables to controller method parameters
  • @ExceptionHandler: Handles exceptions within a single controller
  • @ControllerAdvice: Defines global exception handling, cross-cutting configuration for all controllers

Automatic Parameter Matching

Map request parameters directly to controller method parameters by matching parameter names:

@RequestMapping("/person")
public String handlePersonAuto(String username, double userAge) {
    System.out.println(username + " " + userAge);
    return "hello";
}

Automatic Data Binding

Bind request parameters to a custom model object automatically.

  1. Create the Person model class in com.example.springmvc.model:
package com.example.springmvc.model;

public class Person {
    private String username;
    private int userAge;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public int getUserAge() {
        return userAge;
    }

    public void setUserAge(int userAge) {
        this.userAge = userAge;
    }
}
  1. Add the controller method:
@RequestMapping("/person1")
public String handlePersonBinding(Person person) {
    System.out.println(person.getUsername() + " " + person.getUserAge());
    return "hello";
}

Handle Date Parameters with @InitBinder

Convert string request parameters to Date objects using a custom binder:

import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.support.ServletRequestDataBinder;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.springframework.beans.propertyeditors.CustomDateEditor;

@RequestMapping("/date")
public String handleDate(Date date) {
    System.out.println(date);
    return "hello";
}

@InitBinder
public void initDateBinder(ServletRequestDataBinder binder) {
    binder.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), true));
}

Pass Data to Frontend

Add data to the model map to expose it to the view:

import java.util.Map;
import org.springframework.web.bind.annotation.RequestMapping;

@RequestMapping("/show")
public String passPersonData(Map<String, Object> model) {
    Person person = new Person();
    person.setUsername("jayjay");
    person.setUserAge(20);
    model.put("person", person);
    return "show";
}

Access the person attribute in the JSP view via the request scope.

Ajax Request Handling

Create controller methods to handle AJAX calls, and use jQuery on the frontend:

import java.io.PrintWriter;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/mvc")
public class AjaxController {
    @RequestMapping("/getPerson")
    public void getPerson(String username, HttpServletResponse response) throws Exception {
        PrintWriter writer = response.getWriter();
        writer.write("Hello, " + username);
        writer.flush();
        writer.close();
    }

    @RequestMapping("/name")
    public String showNamePage() {
        return "name";
    }
}

Frontend jQuery code:

$(function(){
    $("#btn").click(function(){
        $.post("mvc/getPerson", {username:$("#nameInput").val()}, function(data){
            alert(data);
        });
    });
});

Redirect Requests

Use the redirect: prefix to perform a redirect:

@RequestMapping("/redirect")
public String handleRedirect() {
    return "redirect:/mvc/hello";
}

File Upload

  1. Add the required Apache Commons FileUpload and IO JARs to your project.
  2. Configure the multipart resolver in spring-mvc-servlet.xml:
<!-- Multipart file upload configuration -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <property name="maxUploadSize" value="104857600" />
</bean>
  1. Create the upload controller method:
import java.io.File;
import java.io.FileOutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

@RequestMapping(value = "/upload", method = RequestMethod.POST)
public String handleFileUpload(HttpServletRequest request) throws Exception {
    MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) request;
    MultipartFile uploadFile = multiRequest.getFile("uploadFile");
    String originalFileName = uploadFile.getOriginalFilename();
    String fileExtension = originalFileName.substring(originalFileName.lastIndexOf('.'));
    String newFileName = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + fileExtension;
    
    String uploadPath = request.getSession().getServletContext().getRealPath("/") + "upload/";
    File targetFile = new File(uploadPath + newFileName);
    if (!targetFile.getParentFile().exists()) {
        targetFile.getParentFile().mkdirs();
    }
    
    uploadFile.transferTo(targetFile);
    return "hello";
}
  1. Frontend form:
<form action="mvc/upload" method="post" enctype="multipart/form-data">
    <input type="file" name="uploadFile"><br>
    <input type="submit" value="Upload File">
</form>

Explicit Parameter Binding with @RequestParam

Bind and validate request parameters explicitly:

package com.example.springmvc.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
@RequestMapping("/test")
public class ParamController {
    @RequestMapping("/param")
    public String handleParam(@RequestParam("userId") Integer userId,
            @RequestParam("username") String username) {
        System.out.println(userId + " " + username);
        return "/hello";
    }
}

RESTful API with Spring MVC

  1. Create a REST controller:
package com.example.springmvc.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
@RequestMapping("/rest")
public class UserRestController {
    @RequestMapping(value = "/user/{userId}", method = RequestMethod.GET)
    public String getUser(@PathVariable("userId") Integer userId) {
        System.out.println("GET request for user: " + userId);
        return "/hello";
    }
    
    @RequestMapping(value = "/user/{userId}", method = RequestMethod.POST)
    public String createUser(@PathVariable("userId") Integer userId) {
        System.out.println("POST request for user: " + userId);
        return "/hello";
    }
    
    @RequestMapping(value = "/user/{userId}", method = RequestMethod.PUT)
    public String updateUser(@PathVariable("userId") Integer userId) {
        System.out.println("PUT request for user: " + userId);
        return "/hello";
    }
    
    @RequestMapping(value = "/user/{userId}", method = RequestMethod.DELETE)
    public String deleteUser(@PathVariable("userId") Integer userId) {
        System.out.println("DELETE request for user: " + userId);
        return "/hello";
    }
}
  1. Enable HTTP method conversion for forms: Add the HiddenHttpMethodFilter to web.xml:
<!-- Convert POST requests to PUT/DELETE for RESTful APIs -->
<filter>
    <filter-name>hiddenMethodFilter</filter-name>
    <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>hiddenMethodFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
  1. Frontend forms for REST requests:
<!-- PUT Request -->
<form action="rest/user/1" method="post">
    <input type="hidden" name="_method" value="PUT">
    <input type="submit" value="Submit PUT Request">
</form>

<!-- POST Request -->
<form action="rest/user/1" method="post">
    <input type="submit" value="Submit POST Request">
</form>

<!-- GET Request -->
<form action="rest/user/1" method="get">
    <input type="submit" value="Submit GET Request">
</form>

<!-- DELETE Request -->
<form action="rest/user/1" method="post">
    <input type="hidden" name="_method" value="DELETE">
    <input type="submit" value="Submit DELETE Request">
</form>

Return JSON Responses

  1. Add Jackson JSON processing dependencies to your project.
  2. Create a JSON controller:
package com.example.springmvc.controller;

import java.util.Date;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.example.springmvc.model.User;

@Controller
@RequestMapping("/json")
public class JsonController {
    
    @ResponseBody
    @RequestMapping("/user")
    public User getUser() {
        User user = new User();
        user.setId(1);
        user.setUsername("jayjay");
        user.setBirthDate(new Date());
        return user;
    }
}

Exception Handling

  1. Local Exception Handling (per controller):
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@ExceptionHandler
public ModelAndView handleLocalException(Exception ex) {
    ModelAndView mv = new ModelAndView("error");
    mv.addObject("exception", ex);
    System.out.println("Local exception handler triggered");
    return mv;
}

@RequestMapping("/error")
public String triggerError() {
    int divideByZero = 5 / 0;
    return "hello";
}
  1. Global Exception Handling with @ControllerAdvice:
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;

@ControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler
    public ModelAndView handleGlobalException(Exception ex) {
        ModelAndView mv = new ModelAndView("error");
        mv.addObject("exception", ex);
        System.out.println("Global exception handler triggered");
        return mv;
    }
}
  1. Global Exception Handling with SimpleMappingExceptionResolver: Add this configuration to spring-mvc-servlet.xml:
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    <property name="exceptionMappings">
        <props>
            <prop key="java.lang.ArithmeticException">error</prop>
        </props>
    </property>
</bean>

The error view is your custom error page.

Custom Interceptor

  1. Create a custom interceptor class:
package com.example.springmvc.interceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

public class CustomInterceptor implements HandlerInterceptor {

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("Interceptor: afterCompletion");
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("Interceptor: postHandle");
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("Interceptor: preHandle");
        return true;
    }
}
  1. Register the interceptor in spring-mvc-servlet.xml:
<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/mvc/**" />
        <bean class="com.example.springmvc.interceptor.CustomInterceptor" />
    </mvc:interceptor>
</mvc:interceptors>
  1. Interceptor Execution Flow: preHandle → Controller Execution → postHandleafterCompletion.

Form Validation and Internationaliaztion

  1. Add Hibernate Validator and related dependencies to your project.

  2. Create a User model with validation annotations:

package com.example.springmvc.model;

import java.util.Date;
import org.hibernate.validator.constraints.NotEmpty;
import org.springframework.format.annotation.DateTimeFormat;
import javax.validation.constraints.Past;

public class User {
    private int id;
    
    @NotEmpty(message = "Username cannot be empty")
    private String username;
    
    @Past
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date birthDate;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Date getBirthDate() {
        return birthDate;
    }

    public void setBirthDate(Date birthDate) {
        this.birthDate = birthDate;
    }
}
  1. Create a JSP form using Spring form tags:
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Add User</title>
</head>
<body>
    <form:form action="form/add" method="post" modelAttribute="user">
        ID: <form:input path="id"/> <form:errors path="id"/><br>
        Username: <form:input path="username"/> <form:errors path="username"/><br>
        Birth Date: <form:input path="birthDate"/> <form:errors path="birthDate"/>
        <input type="submit" value="Submit">
    </form:form>
</body>
</html>
  1. Create the form controller:
package com.example.springmvc.controller;

import javax.validation.Valid;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import com.example.springmvc.model.User;
import java.util.Map;

@Controller
@RequestMapping("/form")
public class FormController {
    @RequestMapping(value = "/add", method = RequestMethod.POST)
    public String addUser(@Valid User user, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            return "addUser";
        }
        return "showUser";
    }
    
    @RequestMapping(value = "/add", method = RequestMethod.GET)
    public String showAddForm(Map<String, Object> model) {
        model.put("user", new User());
        return "addUser";
    }
}
  1. Customize Validation Messages Create validation-messages.properties in src/main/resources:
NotEmpty.user.username=Username cannot be empty
Past.user.birthDate=Birth date must be a past date
DateTimeFormat.user.birthDate=Invalid date format, please use yyyy-MM-dd
typeMismatch.user.id=Invalid ID format

Register the message source in spring-mvc-servlet.xml:

<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
    <property name="basename" value="validation-messages" />
</bean>
  1. Internationalization Create locale-specific property files:
  • messages_zh_CN.properties:
username=账号
password=密码
  • messages.properties:
username=Username
password=Password

Create a locale test JSP:

<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Internationalization Test</title>
</head>
<body>
    <fmt:message key="username"/>
    <br>
    <fmt:message key="password"/>
</body>
</html>

Register the view controller in spring-mvc-servlet.xml:

<mvc:view-controller path="/locale" view-name="locale"/>

Access the page at http://localhost:8080/your-project-name/locale and switch browser language to see translated text.

Integrate Spring IOC and Spring MVC

  1. Create a new package com.example.springmvc.integrate for integration demo.

  2. Create the User model class (same as the validation model above).

  3. Create the UserService class:

package com.example.springmvc.integrate.service;

import org.springframework.stereotype.Component;
import com.example.springmvc.integrate.model.User;

@Component
public class UserService {
    public UserService() {
        System.out.println("UserService constructor initialized");
    }
    
    public void saveUser(User user) {
        System.out.println("Saved user: " + user);
    }
}
  1. Create the integration controller:
package com.example.springmvc.integrate.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import com.example.springmvc.integrate.model.User;
import com.example.springmvc.integrate.service.UserService;

@Controller
@RequestMapping("/integrate")
public class IntegrateController {
    @Autowired
    private UserService userService;
    
    @RequestMapping("/user")
    public String saveUser(@RequestBody User user) {
        System.out.println(user);
        userService.saveUser(user);
        return "hello";
    }
}
  1. Create Spring Core Configuration File Create applicationContext.xml in src/main/resources:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context 
        https://www.springframework.org/schema/context/spring-context-5.3.xsd">

    <!-- Scan for service and component beans, exclude controllers -->
    <context:component-scan base-package="com.example.springmvc.integrate">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
        <context:exclude-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
    </context:component-scan>
</beans>
  1. Configure Spring Context Loader in web.xml:
<!-- Configure Spring application context loader -->
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
</context-param>
  1. Adjust Spring MVC Component Scan Update spring-mvc-servlet.xml to only scan controller components:
<context:component-scan base-package="com.example.springmvc.integrate">
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    <context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
</context:component-scan>

Spring MVC Execution Flow

  1. Client sends a request to the DispatcherServlet
  2. DispatcherServlet queries HandlerMapping instances to find the appropriate controller
  3. DispatcherServlet forwards the request to the identified controller
  4. Controller processes the request and returns a ModelAndView
  5. DispatcherServlet resolves the view using ViewResolver instances
  6. The view renders the response and returns it to the client

Spring MVC vs Struts2

  1. Development Style: Spring MVC is method-based, mapping URLs to specific controller methods. Each method generates a single handler, with method parameters destroyed after execution. Struts2 is class-based, using member variables to handle request parameters, requiring multiple instances.
  2. Scope: Spring MVC uses singleton controllers by default, improving performance. Struts2 uses prototype (non-singleton) scope due to its parameter handling design.
  3. Performance: Spring MVC generally has better performance than Struts2, especially when avoiding Struts2-specific tags and using JSTL instead.

Tags: Spring MVC RESTful API Java Web Development Spring Framework Hibernate Validator

Posted on Fri, 29 May 2026 17:25:53 +0000 by freshrod