Spring MVC详解

Spring MVC简介

Spring MVC是Spring框架的一个模块,专门用于开发Web应用。它实现了模型-视图-控制器(MVC)设计模式,将业务逻辑、数据模型和视图渲染分离,使代码更加简洁、可维护。作为Java领域最流行的Web开发框架之一,Spring MVC提供了强大且灵活的功能支持。

Spring MVC工作流程

Spring MVC工作流程

  1. 客户端(浏览器)发送请求到前端控制器DispatcherServlet
  2. DispatcherServlet根据请求信息调用HandlerMapping,解析请求对应的Handler
  3. 解析到对应的Handler(Controller控制器)后,由HandlerAdapter适配器处理
  4. HandlerAdapter执行Handler,返回ModelAndView对象(包含模型数据和视图名)
  5. ViewResolver解析视图名,查找实际的View对象
  6. View使用模型数据渲染结果,通过DispatcherServlet返回给客户端

配置Spring MVC

XML配置方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<web-app>
<!-- 配置DispatcherServlet -->
<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-config.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>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<!-- spring-mvc-config.xml -->
<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">

<!-- 启用注解驱动 -->
<mvc:annotation-driven/>

<!-- 组件扫描 -->
<context:component-scan base-package="com.example.controller"/>

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

<!-- 静态资源处理 -->
<mvc:resources mapping="/resources/**" location="/resources/"/>
</beans>

Java配置方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Configuration
@EnableWebMvc
@ComponentScan("com.example.controller")
public class WebConfig implements WebMvcConfigurer {

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

@Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
return resolver;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

@Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] { RootConfig.class };
}

@Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] { WebConfig.class };
}

@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}

控制器(Controller)开发

基本Controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Controller
public class UserController {

private final UserService userService;

@Autowired
public UserController(UserService userService) {
this.userService = userService;
}

@RequestMapping(value = "/users", method = RequestMethod.GET)
public String listUsers(Model model) {
model.addAttribute("users", userService.getAllUsers());
return "users/list"; // 返回视图名
}

@RequestMapping(value = "/users/{id}", method = RequestMethod.GET)
public String getUserById(@PathVariable("id") Long id, Model model) {
model.addAttribute("user", userService.getUserById(id));
return "users/detail";
}
}

REST风格Controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
@RestController
@RequestMapping("/api/users")
public class UserRestController {

private final UserService userService;

@Autowired
public UserRestController(UserService userService) {
this.userService = userService;
}

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

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

@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public User createUser(@RequestBody User user) {
return userService.createUser(user);
}

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

@DeleteMapping("/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void deleteUser(@PathVariable Long id) {
userService.deleteUser(id);
}
}

请求处理注解

注解 描述
@RequestMapping 为控制器类或方法指定URL映射
@GetMapping 处理HTTP GET请求
@PostMapping 处理HTTP POST请求
@PutMapping 处理HTTP PUT请求
@DeleteMapping 处理HTTP DELETE请求
@PatchMapping 处理HTTP PATCH请求
@PathVariable 获取URL路径中的变量
@RequestParam 获取请求参数
@RequestBody 获取请求体并转换为Java对象
@RequestHeader 获取请求头
@ModelAttribute 添加属性到模型中或绑定表单参数
@SessionAttribute 获取Session属性
@CookieValue 获取Cookie值
@ResponseBody 标记方法返回值直接写入响应体
@ResponseStatus 定义HTTP响应状态码

表单处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@Controller
@RequestMapping("/register")
public class RegisterController {

private final UserService userService;

@Autowired
public RegisterController(UserService userService) {
this.userService = userService;
}

// 显示注册表单
@GetMapping
public String showRegistrationForm(Model model) {
model.addAttribute("user", new UserForm());
return "register";
}

// 处理表单提交
@PostMapping
public String processRegistration(@Valid @ModelAttribute("user") UserForm userForm,
BindingResult result) {
// 表单验证失败
if (result.hasErrors()) {
return "register";
}

// 处理注册逻辑
userService.register(userForm);

return "redirect:/login";
}
}

文件上传

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@Controller
public class FileUploadController {

@PostMapping("/upload")
public String handleFileUpload(@RequestParam("file") MultipartFile file,
RedirectAttributes redirectAttributes) {

if (file.isEmpty()) {
redirectAttributes.addFlashAttribute("message", "请选择要上传的文件");
return "redirect:/uploadForm";
}

try {
String filename = file.getOriginalFilename();
byte[] bytes = file.getBytes();
Path path = Paths.get("uploads/" + filename);
Files.write(path, bytes);

redirectAttributes.addFlashAttribute("message",
"成功上传文件 '" + filename + "'");

} catch (IOException e) {
e.printStackTrace();
}

return "redirect:/uploadStatus";
}
}

异常处理

@ExceptionHandler方法

1
2
3
4
5
6
7
8
9
10
11
12
@Controller
public class UserController {

@ExceptionHandler(UserNotFoundException.class)
public ModelAndView handleUserNotFoundException(UserNotFoundException ex) {
ModelAndView mav = new ModelAndView("error/not-found");
mav.addObject("message", ex.getMessage());
return mav;
}

// 控制器方法...
}

全局异常处理器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@ControllerAdvice
public class GlobalExceptionHandler {

@ExceptionHandler(Exception.class)
public ModelAndView handleGlobalException(Exception ex) {
ModelAndView mav = new ModelAndView("error/generic");
mav.addObject("message", ex.getMessage());
return mav;
}

@ExceptionHandler(UserNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public ModelAndView handleUserNotFoundException(UserNotFoundException ex) {
ModelAndView mav = new ModelAndView("error/not-found");
mav.addObject("message", ex.getMessage());
return mav;
}

@ExceptionHandler(DataAccessException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ModelAndView handleDataAccessException(DataAccessException ex) {
ModelAndView mav = new ModelAndView("error/database");
mav.addObject("message", "数据库访问错误");
return mav;
}
}

拦截器(Interceptor)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class LoggingInterceptor implements HandlerInterceptor {

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
System.out.println("请求前调用: " + request.getRequestURI());
return true; // 返回true才会继续执行
}

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("控制器执行后调用: " + request.getRequestURI());
}

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
System.out.println("视图渲染后调用: " + request.getRequestURI());
}
}

注册拦截器:

1
2
3
4
5
6
7
8
9
10
11
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoggingInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/admin/**");
}
}

总结

Spring MVC是一个强大的Web开发框架,提供了完整的MVC实现,使开发人员可以轻松构建可扩展的Web应用程序。它的特点包括:

  1. 清晰的角色分离,遵循MVC设计模式
  2. 灵活的配置选项(XML或Java配置)
  3. 强大的注解支持,减少样板代码
  4. 与Spring生态系统的无缝集成
  5. 丰富的功能支持:表单处理、文件上传、异常处理等
  6. REST API支持

随着微服务架构和前后端分离的兴起,Spring MVC被广泛用于构建RESTful服务,而Spring Boot更是简化了Spring MVC的配置,使开发者可以专注于业务逻辑的实现。