【Spring】7.深入理解Spring MVC:从基础概念到高级特性的全面解析

Spring MVC是Spring框架的一个模块,全称为Spring Model-View-Controller,是一个基于Java的实现MVC(Model-View-Controller)设计模式的请求驱动类型的轻量级Web框架。MVC模式是一种用于将应用程序分为三个核心组件的软件设计模式,以便分离内聚关注点,从而使得开发、测试和维护更加容易。

一.基本概念

Model(模型):负责数据和业务逻辑,即数据的增删改查等操作。View(视图):负责展示数据(模型)并且可以提供用户交互的界面,通常是指用户看到的界面。Controller(控制器):接收用户请求并将其委托给Model和View去处理,Controller本身不输出任何东西和做任何处理,它只是接收请求并决定调用哪个Model构件去处理请求,然后再确定用哪个View来显示返回的数据。

二.作用

在Web应用中,Spring MVC的作用主要体现在以下几个方面:

解耦:将视图层和业务逻辑层分离,提高代码的可维护性。灵活的视图支持:支持多种视图技术,如JSP、FreeMarker、Thymeleaf等,允许开发者根据项目需求灵活选择。请求驱动:以请求为中心,通过注解或配置将请求映射到对应的处理器(Controller中的方法)。支持RESTful:支持RESTful风格的Web服务开发,简化了API的创建。集成Spring生态:作为Spring大家族的一部分,Spring MVC能够无缝集成Spring的其他模块,如Spring Security、Spring Data等。组件化:通过DispatcherServlet、HandlerMapping、ViewResolver等组件,提供了一个完整的请求处理流程。

三.工作流程

Spring MVC的工作原理大致如下:

用户发送请求到服务器,这个请求首先被DispatcherServlet(前端控制器)捕获。DispatcherServlet将请求委托给HandlerMapping,由它解析请求并映射到相应的Controller。Controller执行业务逻辑后,返回一个ModelAndView对象,其中包含模型数据和要渲染的视图。HandlerAdapter将ModelAndView传递给DispatcherServlet。DispatcherServlet将ModelAndView传递给ViewResolver。ViewResolver解析视图逻辑名,找到具体的视图实现。视图将模型数据渲染成HTML,响应给客户端。

Spring MVC通过组件化和注解化的方式,简化了Web应用的开发过程,提高了开发效率和应用的可维护性。

Spring MVC的常用注解可以分为以下几类,每类都包含了注解的作用、使用案例以及使用时需要注意的事项:

四.常用注解

控制器定义注解

@Controller

作用:标识该类是一个Spring MVC控制器。使用案例:@Controller

public class MyController {

@GetMapping("/greeting")

public String sayHello() {

return "greeting";

}

}

注意:控制器中的方法通常设计为线程安全的无状态方法。 @RestController

作用:组合了@Controller和@ResponseBody,用于类上,意味着该类中的所有方法都默认带有@ResponseBody效果,直接返回数据。使用案例:@RestController

public class MyRestController {

@GetMapping("/greeting")

public String sayHello() {

return "Hello, World!";

}

}

注意:通常用于RESTful Web服务,无需返回视图。

请求映射注解

@RequestMapping

作用:将HTTP请求映射到特定的方法上。使用案例:@Controller

public class MyMappingController {

@RequestMapping(value = "/hello", method = RequestMethod.GET)

public String sayHello(Model model) {

model.addAttribute("message", "Hello, Spring MVC!");

return "hello";

}

}

注意:可以指定请求方法和路径,也可以有额外的属性如请求参数或请求头。 @GetMapping, @PostMapping, @PutMapping, @DeleteMapping

作用:分别用于处理GET、POST、PUT、DELETE请求的简便方式。使用案例:@RestController

public class MyRestfulController {

@GetMapping("/users/{id}")

public User getUserById(@PathVariable Long id) {

// 返回用户信息

return userService.findById(id);

}

}

注意:它们是@RequestMapping的特化,限制了HTTP请求类型。

请求参数注解

@RequestParam

作用:将请求参数与方法参数绑定。使用案例:public String searchByKeyword(@RequestParam String keyword, Model model) {

// 根据关键词进行搜索

model.addAttribute("results", searchService.search(keyword));

return "search-results";

}

注意:可以指定请求参数是否是必需的,默认值是什么。 @PathVariable

作用:从URI路径模板中提取变量。使用案例:public String getUserDetails(@PathVariable("username") String username, Model model) {

// 根据用户名获取用户详情

model.addAttribute("user", userService.findByUsername(username));

return "user-details";

}

注意:通常与@RequestMapping一起使用,用于RESTful URI设计。

请求体注解

@RequestBody

作用:允许将请求正文中的数据绑定到方法参数上。使用案例:@PostMapping("/users")

public User createUser(@RequestBody User user) {

// 创建用户

return userService.create(user);

}

注意:常用于接收JSON格式的请求体,需要配置适当的转换器。 @ResponseBody

作用:指示方法的返回值作为HTTP响应正文返回。使用案例:@GetMapping("/users/{id}")

public User getUserById(@PathVariable Long id) {

// 获取用户信息

return userService.findById(id);

}

注意:通常用于RESTful Web服务,返回值将被序列化为JSON或XML。

异常处理注解

@ExceptionHandler

作用:用于全局或特定异常的处理。使用案例:@ControllerAdvice

public class GlobalExceptionHandler {

@ExceptionHandler(NotFoundException.class)

public ResponseEntity handleNotFoundException() {

return ResponseEntity.notFound().build();

}

}

注意:可以捕获并处理控制器中抛出的异常。

数据注解

@ModelAttribute

作用:用于将请求参数或Session中的对象绑定到模型中。使用案例:public String showForm(@ModelAttribute("user") User user) {

// 显示表单

return "user-form";

}

注意:可以用于表单数据的初始化。

安全性注解

@CrossOrigin

作用:用于跨域资源共享。使用案例:@RestController

@CrossOrigin(origins = "http://localhost:4200")

public class MyRestController {

// ...

}

注意:应谨慎使用,以避免开放不受限制的外部访问。

@ControllerAdvice 或 @RestControllerAdvice

作用:定义全局的控制器增强功能,如异常处理器、数据预处理等。使用案例:@ControllerAdvice

public class GlobalExceptionHandler {

@ExceptionHandler(Exception.class)

public ResponseEntity handleException(Exception e) {

// 全局异常处理逻辑

}

}

注意:可以集中处理全局的控制器相关事宜。使用全局注解时,需要注意它们之间的相互影响,确保全局配置符合应用程序的整体架构和设计原则。此外,全局配置的更改可能会影响到整个应用程序的行为,因此在进行全局配置时需要谨慎。

这些注解的组合使用,为Spring MVC应用程序提供了强大的请求映射能力和数据处理能力。在使用过程中,应根据实际需求和最佳实践来选择和配置注解,以确保应用程序的安全性和高效性。

五.异常处理机制

Spring MVC的异常处理机制是一种强大且灵活的错误处理方式,允许开发者捕获和处理应用程序中的异常,以提供更友好的反馈给客户端。以下是Spring MVC处理异常的详细解释和如何自定义异常处理器的步骤:

处理异常的基本流程

异常抛出:

当Spring MVC中的Controller处理方法执行业务逻辑时,如果遇到任何问题导致异常被抛出,该异常会被Spring MVC框架捕获。 查找异常处理器:

Spring MVC会查找是否有匹配的异常处理器可以处理这个异常。异常处理器是带有@ExceptionHandler注解的方法。 执行异常处理器:

如果找到合适的异常处理器,Spring MVC会调用该处理器中的相关方法来处理异常。 返回错误响应:

异常处理器负责生成一个适当的响应,可以是一个错误页面、一个JSON对象、一个HTML字符串,或者任何其他类型的响应体。 无匹配的异常处理器:

如果没有找到匹配的异常处理器,Spring MVC将使用其默认的异常处理策略。

自定义异常处理器

定义异常处理器类:

创建一个新的类,使用@ControllerAdvice注解来声明这是一个异常处理器类。这个类可以捕获所有Spring MVC控制器抛出的异常。

@ControllerAdvice

public class GlobalExceptionHandler {

// 这里可以定义异常处理方法

}

定义异常处理方法:

在异常处理器类中定义方法,使用@ExceptionHandler注解指定可以处理的异常类型。

@ExceptionHandler(MethodArgumentNotValidException.class)

public ResponseEntity handleValidationException(MethodArgumentNotValidException ex) {

// 构建并返回一个包含验证错误信息的响应实体

String errorMessage = "Validation failed: " + ex.getBindingResult().toString();

return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorMessage);

}

处理方法的返回值:

异常处理方法可以返回多种类型的值,如ModelAndView、View、String、ResponseEntity等。 配置异常处理顺序:

如果有多个异常处理器可以处理同一个异常,可以通过@Order注解或实现Ordered接口来指定处理顺序。 使用ResponseEntity:

使用ResponseEntity可以自定义响应的状态码、头部信息以及响应体。 全局与局部异常处理器:

@ControllerAdvice可以用于全局异常处理,也可以限制在特定的包或控制器上使用@Profile或@Component注解来定义局部异常处理器。 异常处理的响应体:

对于@RestController或@RestControllerAdvice注解的类,异常处理方法的返回值会直接作为响应体返回,无需显式使用@ResponseBody注解。 自定义异常:

可以创建自定义异常类,使得异常处理器能够更精细地处理特定的业务场景。 日志记录:

在异常处理方法中,可以记录异常日志,便于问题的追踪和调试。

通过自定义异常处理器,可以控制异常信息的呈现方式,提供更友好的错误提示,同时确保API的安全性和稳定性。自定义异常处理器是RESTful API设计中不可或缺的一部分,它提升了用户体验并有助于API的健壮性。

六.高级特性

Spring MVC 提供了一系列高级特性,用以增强应用程序的功能和性能。以下是一些重要的高级特性及其用途、使用方法和注意事项:

拦截器(Interceptor)

用途:拦截器用于在请求处理前后执行通用的处理逻辑,如日志记录、安全验证、事务管理等。

使用案例:

public class MyInterceptor implements HandlerInterceptor {

@Override

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {

// 在请求处理之前执行的操作,如权限检查

return true; // 返回 true 表示继续执行请求,返回 false 表示中断请求

}

@Override

public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {

// 请求处理之后执行的操作,如统一的响应格式处理

}

@Override

public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {

// 请求完成后执行的操作,如资源清理

}

}

@Configuration

public class WebConfig implements WebMvcConfigurer {

@Override

public void addInterceptors(InterceptorRegistry registry) {

registry.addInterceptor(new MyInterceptor())

.addPathPatterns("/**") // 所有路径

.excludePathPatterns("/css/**", "/js/**"); // 排除静态资源路径

}

}

注意:拦截器可能会影响性能,需要谨慎使用。拦截器的执行顺序可以在注册时指定。

过滤器(Filter)

用途:过滤器用于在请求进入DispatcherServlet之前进行预处理,如日志记录、安全控制、监控等。

使用案例:

@WebFilter("/*")

public class MyFilter implements Filter {

@Override

public void init(FilterConfig filterConfig) {

// 初始化过滤器

}

@Override

public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) {

// 在请求进入DispatcherServlet之前执行的操作

filterChain.doFilter(servletRequest, servletResponse); // 继续过滤器链

// 在请求离开DispatcherServlet之后执行的操作

}

@Override

public void destroy() {

// 销毁过滤器

}

}

注意:过滤器主要用于处理通用的预处理和后处理逻辑,不涉及请求映射。

格式化器(Formatter)

用途:格式化器用于自定义数据的格式化和解析,特别是对于日期、数字等类型的数据。

使用案例:

public class MyFormatter implements Formatter {

@Override

public Date parse(String text, Locale locale) throws ParseException {

// 实现字符串到日期的转换逻辑

return new SimpleDateFormat("yyyy-MM-dd").parse(text);

}

@Override

public String print(Date object, Locale locale) {

// 实现日期到字符串的转换逻辑

return new SimpleDateFormat("yyyy-MM-dd").format(object);

}

}

@Configuration

public class FormatterConfig {

@Bean

public Formatter dateFormatter() {

return new MyFormatter();

}

}

注意:格式化器需要注册到Spring MVC的格式化器工厂中,并且与字段的注解结合使用。

异步请求处理

用途:异步请求处理允许应用程序在后台线程中处理长时间运行的任务,而不阻塞请求线程。

使用案例:

@Controller

public class AsyncController {

@RequestMapping(value = "/async", method = RequestMethod.GET)

public Callable handleRequest() {

return () -> {

// 执行耗时操作

Thread.sleep(1000); // 模拟耗时操作

return "asyncResult";

};

}

}

注意:异步请求处理需要配置线程池,并且要处理好异常和超时情况。可以使用@Async注解在方法上启用异步执行。

这些高级特性在Spring MVC中发挥着重要作用,它们提供了一种灵活的方式来增强应用程序的功能和性能。在使用这些特性时,需要仔细考虑它们对应用程序的影响,并确保它们被正确配置和使用。

七.Spring MVC与Spring Boot的关系

Spring MVC 和 Spring Boot 的关系可以概括为:

补充关系:Spring MVC 是 Spring 框架的一部分,专注于 Web 应用的模型-视图-控制器模式。Spring Boot 是一个独立的项目,提供了简化的初始搭建以及开发过程。

简化配置:Spring Boot 通过自动配置和起步依赖(starters)减少了传统 Spring MVC 应用所需的大量配置。

集成使用:Spring Boot 并没有取代 Spring MVC,而是与其集成,让开发者可以轻松地在 Spring Boot 应用中使用 Spring MVC。

快速开发:Spring Boot 鼓励使用约定优于配置的原则,加快了开发速度。

微服务支持:Spring Boot 与 Spring Cloud 等微服务项目配合,支持构建微服务应用。

在实践中,Spring Boot 应用通常会包含 Spring MVC 作为处理 Web 请求的组件,Spring Boot 提供了对 Spring MVC 的自动配置支持,使得开发者可以快速搭建 RESTful API 或传统的 Web 应用。

Spring MVC作为一个功能强大且灵活的Web框架,为Java开发者提供了构建高效、可维护Web应用的坚实基础。从基础的MVC模式到高级的异步处理和格式化,Spring MVC不断扩展其能力,以适应快速变化的Web开发需求。同时,Spring Boot的出现进一步简化了Spring MVC的应用,让开发者能够更加专注于业务逻辑的实现。