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);
}
参数解析过程:
@PathVariable
:从URL路径中提取参数/users/123
→id=123
@RequestParam
:从查询字符串中提取参数?name=John
→name="John"
@RequestHeader
:从请求头中提取Authorization: Bearer token123
→token="Bearer token123"
- 类型转换:字符串"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. 请求处理流程总结
- 请求到达:客户端发送HTTP请求到服务器
- 前端控制器:DispatcherServlet接收请求
- 处理器映射:HandlerMapping根据URL找到对应的Controller方法
- 处理器适配:HandlerAdapter准备调用Controller方法
- 参数解析:解析并绑定请求参数到方法参数
- 数据验证:对输入数据进行验证
- 方法执行:通过反射调用Controller方法
- 返回处理:处理方法返回值
- 视图解析:如果需要,解析视图名称为具体视图
- 视图渲染:渲染视图生成响应内容
- 响应返回:将结果返回给客户端
整个流程中,Spring MVC通过各种组件的协同工作,大大简化了Web开发的复杂性,让开发者能够专注于业务逻辑的实现。