Spring MVC

Published 2026-07-30 19:35 Updated 2026-10-13 17:14 1374 words 7 min read ... Page views AI-reviewed An AI proof-read this post, but it still deserves a careful read. AI-rewritten Someone else wrote the original; AI helped me rewrite and polish it.

AI-reviewed An AI proof-read this post, but it still deserves a careful read.

This article describes in detail the core execution process and key component responsibilities of Spring MVC, including front-end controller DispatcherServlet, processor mapping, parameter binding, view skipping, static resource processing and other mechanisms. It focuses on practical development scenarios such as request mapping, parameter binding, JSON data interaction, cross-domain processing, and file uploading, and emphasizes front-end separation, security and best practices, such as using annotations, reasonably configuring CORS, and avoiding direct exposure. File upload path, etc.

Spring MVC

image-001
image-001

Core execution process

When Spring MVC processes requests, the main components and responsibilities are as follows:

  • DispatcherServlet: Front-end controller that uniformly receives requests and coordinates subsequent processing processes.
  • HandlerMapping: Find the corresponding processor method based on the request address.
  • HandlerAdapter: Call the processor in a unified way and complete parameter analysis and other work.
  • Handler: The controller method that actually handles the request.
  • ModelAndView: Encapsulates both model data and logical view names.
  • ViewResolver: Parses logical view names into specific views.
  • View: Responsible for rendering response content.

Build a traditional Spring MVC project

Creating the Maven Web Project

Introduce Spring MVC dependencies. The following versions only correspond to the original course example environment:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.4.RELEASE</version>
</dependency>

configured DispatcherServlet

in web.xml Register the front-end controller in:

<servlet>
    <servlet-name>spring</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring-servlet.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>spring</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

/ said let DispatcherServlet Handle most requests except for special requests inside the container, while still processing static resources by the default Servlet. Configuration to /*, because it can affect container behavior such as JSP forwarding.

Create a Spring MVC configuration file

<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.xsd
           http://www.springframework.org/schema/mvc
           https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <context:component-scan base-package="com.hyxy.controller"/>
    <mvc:annotation-driven/>
</beans>

creation controller

@Controller
public class TestController {
    @RequestMapping("/test")
    @ResponseBody
    public String test() {
        return "test";
    }
}

Access after starting the server /test, the response body will return test。

request map

@RequestMapping

@RequestMapping Classes and methods can be modified. Classes usually represent a uniform address prefix; methods represent specific request mappings.

Common attributes include:

  • value or path: Request an address.
  • method: Allowed request methods.
  • params: Require the request to include or not include the specified parameters.
  • headers: Further restrict the mapping based on the request header.
  • consumes: Limit the media type of the request body.
  • produces: Declare the type of media that responds to.
@Controller
@RequestMapping("/test")
public class TestController {
    @RequestMapping(value = "/add", method = RequestMethod.POST)
    @ResponseBody
    public String add() {
        return "add";
    }
}

In actual projects, more semantically explicit composite annotations can also be used, such as @GetMapping、@PostMapping、@PutMapping and @DeleteMapping。

Request parameter binding

Binding simple parameters

Spring MVC can automatically bind when the request parameter name is the same as the method parameter name. If parameter names are not reserved at compile time, or if you want to specify parameter names explicitly, use the @RequestParam。

@GetMapping("/user")
@ResponseBody
public String getUser(
        @RequestParam(name = "name") String username,
        @RequestParam(name = "age", required = false, defaultValue = "0") Integer age) {
    return username + ":" + age;
}

When using basic types to receive optional parameters, missing parameters will cause type conversion to fail, so optional numeric parameters generally use wrapper types.

Binding path variables

@GetMapping("/add/{name}/{pwd}")
@ResponseBody
public String add(
        @PathVariable("name") String username,
        @PathVariable("pwd") String password) {
    return username + ":" + password;
}

such as access http://localhost:8088/mvc/test/add/tom/123,username has a value of tom,password has a value of 123。

Read request headers and cookies

@GetMapping("/header")
@ResponseBody
public String header(
        @RequestHeader("Accept") String accept,
        @CookieValue(value = "username", defaultValue = "rose") String username) {
    return accept + ":" + username;
}

Binding Java objects

When the request parameter name is the same as the object property name, you can bind the parameter to the object.

@PostMapping("/emp")
@ResponseBody
public Emp add(Emp emp) {
    return emp;
}

For associated objects, you can use property paths to pass parameters. for example Emp contained in Dept dept When, the request parameters can be written as dept.deptName=开发部。

public class Emp {
    private String empNo;
    private String empName;
    private Dept dept;

    // getter 和 setter
}

public class Dept {
    private String deptName;

    // getter 和 setter
}

Get Servlet API objects

When you need to access the original request or session, you can declare it directly in the controller method parameters.

@GetMapping("/request-info")
@ResponseBody
public String requestInfo(HttpServletRequest request, HttpSession session) {
    session.setAttribute("method", request.getMethod());
    return request.getMethod();
}

Business code should not rely too much on the Servlet API. For scenarios that can be completed through annotations and parameter binding, Spring MVC’s parameter parsing mechanism should be preferred.

View jumps and model data

Returns the logical view name

@GetMapping("/books")
public String books(Model model) {
    model.addAttribute("bookList", List.of("Java", "Spring"));
    return "book/list";
}

When paired with the view parser,book/list It will be resolved into a specific page path.

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/views/"/>
    <property name="suffix" value=".jsp"/>
</bean>

return ModelAndView

@GetMapping("/detail")
public ModelAndView detail() {
    ModelAndView modelAndView = new ModelAndView();
    modelAndView.addObject("name", "Spring MVC");
    modelAndView.setViewName("book/detail");
    return modelAndView;
}

Static resource processing

Static resource mappings can be explicitly configured:

<mvc:annotation-driven/>
<mvc:resources mapping="/html/**" location="/html/"/>

You can also use the default Servlet to handle unmatched static resources:

<mvc:default-servlet-handler/>

Separate development of front and rear ends

@RequestBody

@RequestBody The request body is read and the JSON is converted into a Java object through a message converter. The request header should usually contain Content-Type: application/json。

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.14.2</version>
</dependency>
@RestController
@RequestMapping("/test")
public class TestController {
    @PostMapping("/emp")
    public Emp save(@RequestBody Emp emp) {
        return emp;
    }
}
image-002
image-002

JSON field names should match Java attribute names. When field naming is inconsistent, you can use Jackson annotations to map.

@ResponseBody

@ResponseBody Represents writing the method return value to the response body. When returning an object, Spring MVC serializes it into JSON through a message converter; when returning a string, it usually writes the text directly.

@GetMapping("/get")
@ResponseBody
public User get() {
    User user = new User();
    user.setUsername("jack");
    user.setPassword("123456");
    return user;
}

@RestController

@RestController equivalent to class level @Controller with @ResponseBody Combination, suitable for controllers with JSON as the main response content.

Unified response object

public class Result<T> {
    private boolean success;
    private T data;
    private String message;

    public static <T> Result<T> success(T data) {
        Result<T> result = new Result<>();
        result.success = true;
        result.data = data;
        return result;
    }

    public static <T> Result<T> failure(String message) {
        Result<T> result = new Result<>();
        result.success = false;
        result.message = message;
        return result;
    }

    // getter 和 setter
}

cross-domain requests

The browser’s same-origin policy restricts scripts from accessing resources from different sources. The source is determined by the protocol, host and port, and any difference is a cross-source request.

use @CrossOrigin

@RestController
@RequestMapping("/book")
@CrossOrigin(origins = "http://localhost:8081")
public class BookController {
}

Globally configure CORS

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("http://localhost:8081")
                .allowedMethods("GET", "POST", "PUT", "DELETE")
                .allowedHeaders("*")
                .allowCredentials(true)
                .maxAge(3600);
    }
}

When opening credentials, you cannot simply configure the allowed source as a wildcard. The production environment should only open up the sources, methods and requests that are actually needed.

Front-end proxy versus Nginx reverse proxy

Development environments can request through the front-end development server proxy interface; production environments usually use Nginx to unify front-end static resources and back-end interfaces into the same site.

image-003
image-003
image-004
image-004

The reverse proxy only changes the request forwarding method and will not replace the backend’s own authentication, authority verification, and input verification.

file upload

Profile parser

In traditional Spring MVC projects, configurable CommonsMultipartResolver:

<bean id="multipartResolver"
      class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <property name="maxUploadSize" value="5242880"/>
    <property name="defaultEncoding" value="UTF-8"/>
</bean>
<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.4</version>
</dependency>

Forms should use POST Request and set enctype="multipart/form-data"。

Write an upload interface

@RestController
public class FileUploadController {
    private static final Path UPLOAD_DIR = Paths.get("D:/Lesson/uploadFile");

    @PostMapping("/upload")
    public Result<String> upload(@RequestParam("file") MultipartFile file) {
        if (file.isEmpty()) {
            return Result.failure("上传文件不能为空");
        }

        String originalName = file.getOriginalFilename();
        String safeName = originalName == null
                ? UUID.randomUUID().toString()
                : Paths.get(originalName).getFileName().toString();

        try {
            Files.createDirectories(UPLOAD_DIR);
            Path target = UPLOAD_DIR.resolve(safeName).normalize();
            if (!target.startsWith(UPLOAD_DIR)) {
                return Result.failure("文件名不合法");
            }

            file.transferTo(target);
            return Result.success(safeName);
        } catch (IOException e) {
            return Result.failure(e.getMessage());
        }
    }
}

Do not directly trust the original file name submitted by the client. Actual projects should also check file size, extension, MIME type and storage permissions, and avoid exposing the upload directory as an executable directory.

image-005
image-005

Written by AI-rewritten

If you enjoyed this, leave a comment~

Loading...