跳到主要内容

Spring MVC

Spring MVC 是 Spring 框架的 Web 层解决方案,基于 MVC 设计模式,提供了灵活且强大的 Web 应用开发能力。

MVC 模式简介

MVC(Model-View-Controller)是一种将应用程序分离为三个核心组件的设计模式:

Model(模型):代表应用程序的数据和业务逻辑。

View(视图):负责数据的展示。

Controller(控制器):处理用户请求,调用模型,返回视图。

Spring MVC 的核心组件:

组件说明
DispatcherServlet前端控制器,接收所有请求并分发
HandlerMapping请求映射,将请求映射到控制器方法
Controller控制器,处理业务逻辑
ModelAndView封装模型数据和视图信息
ViewResolver视图解析器,解析视图名称到实际视图
View视图,渲染响应内容

请求处理流程

Spring MVC 的请求处理流程如下:

  1. 用户请求发送到 DispatcherServlet
  2. DispatcherServlet 调用 HandlerMapping 找到对应的 Controller
  3. Controller 处理请求,返回 ModelAndView
  4. DispatcherServlet 调用 ViewResolver 解析视图
  5. View 渲染视图,返回响应

配置 Spring MVC

传统配置方式

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd">

<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>
</web-app>

spring-mvc.xml

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

<context:component-scan base-package="com.example.controller"/>

<mvc:annotation-driven/>

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

Java 配置方式

@Configuration
@EnableWebMvc
@ComponentScan("com.example.controller")
public class WebMvcConfig implements WebMvcConfigurer {

@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.jsp("/WEB-INF/views/", ".jsp");
}

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**")
.addResourceLocations("/static/");
}
}

WebApplicationInitializer

public class WebAppInitializer implements WebApplicationInitializer {

@Override
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(WebMvcConfig.class);

DispatcherServlet servlet = new DispatcherServlet(context);
ServletRegistration.Dynamic registration = servletContext.addServlet("dispatcher", servlet);
registration.setLoadOnStartup(1);
registration.addMapping("/");
}
}

控制器

@Controller 和 @RestController

@Controller
public class UserController {

@Autowired
private UserService userService;

@GetMapping("/users")
public String list(Model model) {
model.addAttribute("users", userService.findAll());
return "user/list";
}

@GetMapping("/users/{id}")
public String detail(@PathVariable Long id, Model model) {
model.addAttribute("user", userService.findById(id));
return "user/detail";
}
}

@RestController
@RequestMapping("/api/users")
public class UserApiController {

@Autowired
private UserService userService;

@GetMapping
public List<User> list() {
return userService.findAll();
}

@GetMapping("/{id}")
public User detail(@PathVariable Long id) {
return userService.findById(id);
}

@PostMapping
public User create(@RequestBody User user) {
return userService.save(user);
}

@PutMapping("/{id}")
public User update(@PathVariable Long id, @RequestBody User user) {
user.setId(id);
return userService.save(user);
}

@DeleteMapping("/{id}")
public void delete(@PathVariable Long id) {
userService.delete(id);
}
}

@RestController 相当于 @Controller + @ResponseBody,方法的返回值会自动序列化为 JSON。

请求映射

@Controller
@RequestMapping("/users")
public class UserController {

@GetMapping
public String list() { }

@GetMapping("/{id}")
public String detail(@PathVariable Long id) { }

@PostMapping
public String create(@ModelAttribute User user) { }

@PutMapping("/{id}")
public String update(@PathVariable Long id, @ModelAttribute User user) { }

@DeleteMapping("/{id}")
public String delete(@PathVariable Long id) { }

@GetMapping(params = "name")
public String searchByName(@RequestParam String name) { }

@GetMapping(headers = "X-Requested-With=XMLHttpRequest")
public String ajaxList() { }

@GetMapping(consumes = "application/json")
public String jsonList() { }

@GetMapping(produces = "application/json")
@ResponseBody
public User jsonDetail(@PathVariable Long id) { }
}

请求参数绑定

@RestController
@RequestMapping("/api")
public class ParamController {

@GetMapping("/path/{id}")
public String pathVariable(@PathVariable Long id) {
return "ID: " + id;
}

@GetMapping("/path/{userId}/orders/{orderId}")
public String multiPathVariables(
@PathVariable Long userId,
@PathVariable Long orderId) {
return "User: " + userId + ", Order: " + orderId;
}

@GetMapping("/query")
public String queryParam(
@RequestParam String name,
@RequestParam(defaultValue = "1") int page,
@RequestParam(required = false) String sort) {
return "Name: " + name + ", Page: " + page + ", Sort: " + sort;
}

@GetMapping("/header")
public String header(
@RequestHeader("User-Agent") String userAgent,
@RequestHeader(value = "X-Token", required = false) String token) {
return "User-Agent: " + userAgent + ", Token: " + token;
}

@GetMapping("/cookie")
public String cookie(@CookieValue(value = "JSESSIONID", required = false) String sessionId) {
return "Session ID: " + sessionId;
}

@PostMapping("/body")
public User body(@RequestBody User user) {
return user;
}

@PostMapping("/form")
public User form(@ModelAttribute User user) {
return user;
}
}

数据验证

@Data
public class UserDTO {

@NotBlank(message = "用户名不能为空")
@Size(min = 3, max = 20, message = "用户名长度必须在3-20之间")
private String username;

@NotBlank(message = "邮箱不能为空")
@Email(message = "邮箱格式不正确")
private String email;

@Min(value = 18, message = "年龄必须大于18岁")
private Integer age;
}

@RestController
@RequestMapping("/api/users")
public class UserController {

@PostMapping
public ResponseEntity<?> create(@Valid @RequestBody UserDTO userDTO, BindingResult result) {
if (result.hasErrors()) {
Map<String, String> errors = new HashMap<>();
result.getFieldErrors().forEach(error ->
errors.put(error.getField(), error.getDefaultMessage()));
return ResponseEntity.badRequest().body(errors);
}
return ResponseEntity.ok(userDTO);
}
}

响应处理

返回视图

@Controller
public class ViewController {

@GetMapping("/hello")
public String hello(Model model) {
model.addAttribute("message", "Hello Spring MVC");
return "hello";
}

@GetMapping("/user")
public ModelAndView user() {
ModelAndView mav = new ModelAndView("user");
mav.addObject("user", new User(1L, "张三"));
return mav;
}

@GetMapping("/redirect")
public String redirect() {
return "redirect:/hello";
}

@GetMapping("/forward")
public String forward() {
return "forward:/hello";
}
}

返回 JSON

@RestController
public class ApiController {

@GetMapping("/user")
public User getUser() {
return new User(1L, "张三");
}

@GetMapping("/users")
public List<User> getUsers() {
return Arrays.asList(
new User(1L, "张三"),
new User(2L, "李四")
);
}

@GetMapping("/response-entity")
public ResponseEntity<User> responseEntity() {
User user = new User(1L, "张三");
return ResponseEntity.ok()
.header("X-Custom-Header", "value")
.body(user);
}

@GetMapping("/not-found")
public ResponseEntity<User> notFound() {
return ResponseEntity.notFound().build();
}
}

异常处理

@ControllerAdvice
public class GlobalExceptionHandler {

@ExceptionHandler(UserNotFoundException.class)
public ResponseEntity<Map<String, String>> handleUserNotFound(UserNotFoundException ex) {
Map<String, String> body = new HashMap<>();
body.put("error", "User Not Found");
body.put("message", ex.getMessage());
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(body);
}

@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, String>> handleValidation(MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getFieldErrors().forEach(error ->
errors.put(error.getField(), error.getDefaultMessage()));
return ResponseEntity.badRequest().body(errors);
}

@ExceptionHandler(Exception.class)
public ResponseEntity<Map<String, String>> handleGeneral(Exception ex) {
Map<String, String> body = new HashMap<>();
body.put("error", "Internal Server Error");
body.put("message", ex.getMessage());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(body);
}
}

拦截器

拦截器可以在请求处理前后执行自定义逻辑:

@Component
public class LoggingInterceptor implements HandlerInterceptor {

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
long startTime = System.currentTimeMillis();
request.setAttribute("startTime", startTime);
log.info("请求开始: {} {}", request.getMethod(), request.getRequestURI());
return true;
}

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("请求处理完成: {}", request.getRequestURI());
}

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
long startTime = (Long) request.getAttribute("startTime");
long elapsedTime = System.currentTimeMillis() - startTime;
log.info("请求结束: {}, 耗时: {}ms", request.getRequestURI(), elapsedTime);
}
}

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

@Autowired
private LoggingInterceptor loggingInterceptor;

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loggingInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/static/**", "/error");
}
}

文件上传

@Controller
public class FileUploadController {

@PostMapping("/upload")
@ResponseBody
public String upload(@RequestParam("file") MultipartFile file) throws IOException {
if (file.isEmpty()) {
return "文件为空";
}

String fileName = file.getOriginalFilename();
String filePath = "/uploads/" + UUID.randomUUID() + "_" + fileName;

File dest = new File(filePath);
file.transferTo(dest);

return "上传成功: " + fileName;
}

@PostMapping("/upload-multi")
@ResponseBody
public String uploadMulti(@RequestParam("files") MultipartFile[] files) throws IOException {
StringBuilder result = new StringBuilder();
for (MultipartFile file : files) {
if (!file.isEmpty()) {
result.append(file.getOriginalFilename()).append(", ");
}
}
return "上传文件: " + result.toString();
}
}

配置文件上传:

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

@Bean
public MultipartResolver multipartResolver() {
CommonsMultipartResolver resolver = new CommonsMultipartResolver();
resolver.setMaxUploadSize(10 * 1024 * 1024);
resolver.setDefaultEncoding("UTF-8");
return resolver;
}
}

小结

本章介绍了 Spring MVC 的核心概念和使用方法:

  1. MVC 模式:Model、View、Controller 的分离
  2. 请求处理流程:DispatcherServlet 的分发机制
  3. 控制器:@Controller、@RestController、请求映射
  4. 参数绑定:@PathVariable、@RequestParam、@RequestBody 等
  5. 数据验证:@Valid、BindingResult
  6. 响应处理:视图返回、JSON 返回、ResponseEntity
  7. 异常处理:@ControllerAdvice、@ExceptionHandler
  8. 拦截器:HandlerInterceptor
  9. 文件上传:MultipartFile

Spring MVC 提供了灵活且强大的 Web 开发能力,是构建 Web 应用的基础。