Spring MVC 处理请求的完整流程详解

Spring MVC 处理请求的完整流程详解

Spring MVC 处理一个 HTTP 请求的完整流程涉及多个组件协同工作。下面我将详细说明每个步骤,并举例说明。

1. 整体流程图

复制代码
Client → DispatcherServlet → HandlerMapping → Controller 
       → HandlerAdapter → Controller Method → ModelAndView 
       → ViewResolver → View → Response → Client

2. 详细步骤分析

步骤1:客户端发送请求

http 复制代码
GET /users/123?name=John HTTP/1.1
Host: localhost:8080
Content-Type: application/json
Authorization: Bearer token123

步骤2:DispatcherServlet接收请求

DispatcherServlet是Spring MVC的核心前端控制器,负责接收所有HTTP请求。

java 复制代码
// web.xml配置(传统方式)
<servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring-mvc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

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

// 或者通过Java配置
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

步骤3:HandlerMapping查找处理器

DispatcherServlet使用HandlerMapping来确定哪个Controller应该处理当前请求。

java 复制代码
// Controller示例
@RestController
@RequestMapping("/users")
public class UserController {
    
    @Autowired
    private UserService userService;
    
    @GetMapping("/{id}")
    public User getUser(@PathVariable Long id, 
                       @RequestParam(required = false) String name) {
        return userService.findById(id);
    }
    
    @PostMapping
    public User createUser(@RequestBody User user) {
        return userService.createUser(user);
    }
}

HandlerMapping会根据请求URL /users/123 找到对应的处理器方法 getUser

步骤4:HandlerAdapter调用处理器

HandlerAdapter负责实际调用Controller中的方法。

java 复制代码
// HandlerAdapter处理流程示例
public class RequestMappingHandlerAdapter {
    public ModelAndView handle(HttpServletRequest request, 
                              HttpServletResponse response, 
                              Object handler) throws Exception {
        // 1. 参数解析
        Object[] args = getArguments(request, handler);
        
        // 2. 反射调用Controller方法
        Object result = invokeHandlerMethod(handler, args);
        
        // 3. 处理返回结果
        return getModelAndView(result);
    }
}

步骤5:参数解析和绑定

Spring MVC自动解析和绑定请求参数。

java 复制代码
@GetMapping("/{id}")
public User getUser(
    @PathVariable Long id,                           // 路径变量
    @RequestParam(required = false) String name,     // 查询参数
    @RequestHeader("Authorization") String token,    // 请求头
    @CookieValue("sessionId") String sessionId,      // Cookie值
    HttpServletRequest request) {                    // 原始请求对象
    
    System.out.println("ID: " + id);
    System.out.println("Name: " + name);
    System.out.println("Token: " + token);
    System.out.println("Session ID: " + sessionId);
    
    return userService.findById(id);
}

参数解析过程:

  1. @PathVariable:从URL路径中提取参数 /users/123id=123
  2. @RequestParam:从查询字符串中提取参数 ?name=Johnname="John"
  3. @RequestHeader:从请求头中提取 Authorization: Bearer token123token="Bearer token123"
  4. 类型转换:字符串"123"自动转换为Long类型

步骤6:数据验证

java 复制代码
@PostMapping
public User createUser(@Valid @RequestBody User user, 
                      BindingResult bindingResult) {
    // 验证失败处理
    if (bindingResult.hasErrors()) {
        List<String> errors = bindingResult.getFieldErrors().stream()
            .map(error -> error.getField() + ": " + error.getDefaultMessage())
            .collect(Collectors.toList());
        throw new ValidationException(errors.toString());
    }
    
    return userService.createUser(user);
}

// User实体类
public class User {
    @NotNull(message = "ID不能为空")
    private Long id;
    
    @NotBlank(message = "姓名不能为空")
    @Size(min = 2, max = 50, message = "姓名长度必须在2-50之间")
    private String name;
    
    @Email(message = "邮箱格式不正确")
    private String email;
    
    @Min(value = 0, message = "年龄不能小于0")
    @Max(value = 150, message = "年龄不能大于150")
    private Integer age;
    
    // getters and setters
}

步骤7:执行Controller方法

java 复制代码
@RestController
@RequestMapping("/users")
public class UserController {
    
    @Autowired
    private UserService userService;
    
    @GetMapping("/{id}")
    public ResponseEntity<User> getUser(@PathVariable Long id) {
        User user = userService.findById(id);
        if (user != null) {
            return ResponseEntity.ok(user);
        } else {
            return ResponseEntity.notFound().build();
        }
    }
    
    @PostMapping
    public ResponseEntity<User> createUser(@RequestBody User user) {
        User createdUser = userService.createUser(user);
        // 返回201状态码和创建的资源
        return ResponseEntity.status(HttpStatus.CREATED).body(createdUser);
    }
}

步骤8:处理返回值

Spring MVC支持多种返回值类型:

java 复制代码
@Controller
public class ExampleController {
    
    // 1. 返回ModelAndView
    @RequestMapping("/page1")
    public ModelAndView showPage1() {
        ModelAndView mav = new ModelAndView("user/profile");
        mav.addObject("user", userService.getCurrentUser());
        return mav;
    }
    
    // 2. 返回String(视图名称)
    @RequestMapping("/page2")
    public String showPage2(Model model) {
        model.addAttribute("user", userService.getCurrentUser());
        return "user/profile";
    }
    
    // 3. 返回对象(@ResponseBody自动序列化为JSON)
    @GetMapping("/api/user/{id}")
    @ResponseBody
    public User getUserApi(@PathVariable Long id) {
        return userService.findById(id);
    }
    
    // 4. 使用@RestController自动序列化
    @GetMapping("/api/users")
    public List<User> getAllUsers() {
        return userService.findAll();
    }
    
    // 5. 返回ResponseEntity(包含状态码和响应头)
    @PostMapping("/api/users")
    public ResponseEntity<User> createUserWithResponse(@RequestBody User user) {
        User createdUser = userService.createUser(user);
        return ResponseEntity
            .created(URI.create("/api/users/" + createdUser.getId()))
            .body(createdUser);
    }
}

步骤9:视图解析

如果返回的是视图名称,ViewResolver会解析视图:

java 复制代码
// 视图解析器配置
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
    
    @Bean
    public ViewResolver viewResolver() {
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setPrefix("/WEB-INF/views/");
        resolver.setSuffix(".jsp");
        return resolver;
    }
}

// Controller返回视图名称
@Controller
public class HomeController {
    
    @RequestMapping("/home")
    public String home(Model model) {
        model.addAttribute("message", "Hello Spring MVC!");
        // 返回视图名称"home"
        return "home";
    }
}

// 视图解析过程:
// ViewResolver将"home"解析为"/WEB-INF/views/home.jsp"

步骤10:视图渲染

jsp 复制代码
<!-- /WEB-INF/views/home.jsp -->
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>Home Page</title>
</head>
<body>
    <h1>Welcome</h1>
    <p>${message}</p>
    <c:if test="${not empty user}">
        <p>Hello, ${user.name}!</p>
    </c:if>
</body>
</html>

步骤11:响应返回客户端

java 复制代码
// 最终HTTP响应
HTTP/1.1 200 OK
Content-Type: text/html;charset=UTF-8
Content-Length: 1234

<html>
<head>
    <title>Home Page</title>
</head>
<body>
    <h1>Welcome</h1>
    <p>Hello Spring MVC!</p>
</body>
</html>

3. 完整示例演示

让我们通过一个完整的示例来演示整个流程:

java 复制代码
// 1. 实体类
public class Product {
    private Long id;
    private String name;
    private BigDecimal price;
    private String category;
    
    // constructors, getters, setters
}

// 2. Service层
@Service
public class ProductService {
    
    public Product findById(Long id) {
        // 模拟数据库查询
        return new Product(id, "Product " + id, new BigDecimal("99.99"), "Electronics");
    }
    
    public List<Product> findAll() {
        return Arrays.asList(
            new Product(1L, "Laptop", new BigDecimal("1299.99"), "Electronics"),
            new Product(2L, "Book", new BigDecimal("29.99"), "Education")
        );
    }
    
    public Product create(Product product) {
        product.setId(System.currentTimeMillis()); // 简单生成ID
        return product;
    }
}

// 3. Controller层
@RestController
@RequestMapping("/api/products")
public class ProductController {
    
    @Autowired
    private ProductService productService;
    
    // GET /api/products/1
    @GetMapping("/{id}")
    public ResponseEntity<Product> getProduct(@PathVariable Long id) {
        System.out.println("Processing request for product ID: " + id);
        Product product = productService.findById(id);
        if (product != null) {
            return ResponseEntity.ok(product);
        } else {
            return ResponseEntity.notFound().build();
        }
    }
    
    // GET /api/products?category=Electronics&page=0&size=10
    @GetMapping
    public ResponseEntity<List<Product>> getAllProducts(
            @RequestParam(required = false) String category,
            @RequestParam(defaultValue = "0") int page,
            @RequestParam(defaultValue = "10") int size) {
        
        System.out.println("Getting products - Category: " + category + 
                          ", Page: " + page + ", Size: " + size);
        
        List<Product> products = productService.findAll();
        return ResponseEntity.ok(products);
    }
    
    // POST /api/products
    @PostMapping
    public ResponseEntity<Product> createProduct(@Valid @RequestBody Product product) {
        System.out.println("Creating product: " + product.getName());
        Product createdProduct = productService.create(product);
        return ResponseEntity
            .created(URI.create("/api/products/" + createdProduct.getId()))
            .body(createdProduct);
    }
}

// 4. 全局异常处理
@ControllerAdvice
public class GlobalExceptionHandler {
    
    @ExceptionHandler(Exception.class)
    public ResponseEntity<String> handleException(Exception e) {
        return ResponseEntity.status(500).body("Internal Server Error: " + e.getMessage());
    }
}

4. 请求处理流程总结

  1. 请求到达:客户端发送HTTP请求到服务器
  2. 前端控制器:DispatcherServlet接收请求
  3. 处理器映射:HandlerMapping根据URL找到对应的Controller方法
  4. 处理器适配:HandlerAdapter准备调用Controller方法
  5. 参数解析:解析并绑定请求参数到方法参数
  6. 数据验证:对输入数据进行验证
  7. 方法执行:通过反射调用Controller方法
  8. 返回处理:处理方法返回值
  9. 视图解析:如果需要,解析视图名称为具体视图
  10. 视图渲染:渲染视图生成响应内容
  11. 响应返回:将结果返回给客户端

整个流程中,Spring MVC通过各种组件的协同工作,大大简化了Web开发的复杂性,让开发者能够专注于业务逻辑的实现。