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 的请求处理流程如下:
- 用户请求发送到 DispatcherServlet
- DispatcherServlet 调用 HandlerMapping 找到对应的 Controller
- Controller 处理请求,返回 ModelAndView
- DispatcherServlet 调用 ViewResolver 解析视图
- 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 的核心概念和使用方法:
- MVC 模式:Model、View、Controller 的分离
- 请求处理流程:DispatcherServlet 的分发机制
- 控制器:@Controller、@RestController、请求映射
- 参数绑定:@PathVariable、@RequestParam、@RequestBody 等
- 数据验证:@Valid、BindingResult
- 响应处理:视图返回、JSON 返回、ResponseEntity
- 异常处理:@ControllerAdvice、@ExceptionHandler
- 拦截器:HandlerInterceptor
- 文件上传:MultipartFile
Spring MVC 提供了灵活且强大的 Web 开发能力,是构建 Web 应用的基础。