Skip to content

Spring MVC

字数: 0 字 时长: 0 分钟

简介

Spring MVC 是指 MVC(Model-View-Controller)模式,它将请求处理流程分为三层:模型层视图层控制层,用一种松耦合的方式将用户请求业务逻辑视图渲染分离开来。

Spring MVC 与 Spring 的关系

Spring 是底层基础,Spring MVC 构建在 Spring 核心之上,利用其提供的 IOC/DI、AOP 等功能来实现 Web 层的管理

工作流程

  1. DispatcherServlet 接收 HTTP 请求

DispatcherServlet (作为整个流程的调度中心)会首先接收到 HTTP 请求,然后调用 doDispatch() 执行分发调度逻辑

MVC1.webp

  1. 通过 HandlerMapping 解析请求映射

DispatcherServlet 会首先去请求 HandlerMapping ,返回一个 HandlerExecutionChain 处理器执行链

MVC2.webp

其实这个 HandlerExecutionChain 对象包含了一个处理器和一系列拦截器

MVC3.webp

  1. HandlerAdapter 适配器反射执行实际处理器方法

先通过 HandlerExecutionChain 找到对应的 HandlerAdapter

MVC4.webp

实际执行之前,还会调用拦截器的 preHandle() 方法,返回为 true 的情况下,才会执行处理器方法,返回 ModelAndView

MVC5.webp

  1. 处理 ModelAndView

ModelAndView 委托给 ViewResolver 进行解析渲染

MVC6.webp

核心组件

DispatcherServlet

DispatcherServlet 是请求的入口与出口,也是整个流程的中央调度器,将请求委托给其他组件处理,比如 HandlerMappingHandlerAdapterViewResolver

HandlerMapping

处理器映射器:负责解析请求,匹配请求 URL 对应的 Controller (或Handler)以及关联的拦截器 (HandlerInterceptor)链

HandlerAdapter

处理器适配器:适配器模式实现,负责调用目标 Handler 的相应方法(如@RequestMapping注解的方法),将请求参数、模型数据等进行封装,最后返回 ModelAndView 对象

Controller / Handler

实际处理器,负责接收 HandlerAdapter 传递过来的请求数据,执行业务逻辑(通常需要调用 Service 层)

ViewResolver / View

视图解析器与视图,将 Handler 返回的逻辑视图解析为具体的视图对象,并使用具体的视图模板进行渲染

HandlerExceptionResolver

处理器异常解析器,负责处理请求处理过程中发生的异常,可以将异常映射到特定的错误页面。常见的实现是 @ControllerAdvice + @ExceptionHandler

Spring 父子容器

父容器指的是 Spring 的根容器,主要管理应用程序的全局 Bean,如服务层(Service)、数据访问层(DAO)等。通过 ContextLoaderListener 在应用启动时就初始化。

子容器指的是 Spring MVC 中的 DispatcherServlet 创建的容器,用于管理 Web 层相关 Bean (如 ControllerHandlerMapping);在 DispatcherServlet 启动时初始化。

父子容器设计的好处

子容器可以访问父容器,父容器不能访问子容器。开发者可以将业务逻辑和 Web 层分离,避免不必要的耦合。

拦截器

拦截器 (Interceptor)在 Spring MVC 的请求处理流程中起到类似过滤器的作用,但比过滤器更灵活。

  • 过滤器Servlet 规范定义,可以过滤所有请求,包括静态资源、Servlet、JSP、Controller 请求,类似门卫的角色
  • 拦截器Spring MVC 框架定义,只对 Controller 层请求起作用,提供更细粒度的控制,类似内部各流程关卡

拦截器提供三个核心方法:

  1. preHandle():在控制器方法执行之前调用,如果返回 false 则不会调用控制器方法
  2. postHandle():在控制器方法执行之后,视图渲染之前调用。
  3. afterCompletion():在视图渲染完成后调用,用于清理资源或记录执行时间等

拦截器配置案例

java
public class MyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 在处理器方法调用前进行拦截
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        // 处理器方法执行后,但视图渲染前执行
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 视图渲染后执行
    }
}
java
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new MyInterceptor())
                .addPathPatterns("/**") // 拦截所有路径
                .excludePathPatterns("/login", "/error"); // 排除某些路径
    }
}

Spring MVC 拦截器与 Spring AOP 拦截器的区别

Spring MVC 拦截器与 Spring AOP 拦截器其实完全不是一个东西!不要混淆

  • AOP 拦截器基于动态代理实现,拦截的是方法调用
  • MVC 拦截器通过实现 HandlerInterceptor 接口定义拦截逻辑,用于增强 Web 层的 HTTP 请求流程

常用注解

请求映射相关

@RequestMapping@GetMapping@PostMapping@PutMapping@DeleteMapping@PatchMapping 处理相对应的请求

参数绑定相关

java
// 获取 URL 路径中的变量
@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id)
java
// 	绑定单个字段
public List<User> search(@RequestParam String keyword)
java
// 将请求体(JSON/XML)解析为 Java 对象
@PostMapping
public User createUser(@RequestBody User user)
java
// 将请求参数绑定到对象 (常用于表单提交)
public String submitForm(@ModelAttribute UserForm form)
java
// 处理文件上传
public String upload(@RequestPart("file") MultipartFile file)
java
// 将 HTTP 请求头的值注入到控制器中
@GetMapping("/header-info")
public String getHeaderInfo(@RequestHeader("User-Agent") String userAgent) {
    // 使用 userAgent 进行业务处理
    return "headerInfoView";
}
java
// 从 HTTP 请求的 Cookie 中提取值,注入到控制器方法的参数中
@GetMapping("/cookie-info")
public String getCookieInfo(@CookieValue("sessionId") String sessionId) {
    // 使用 sessionId 进行业务处理
    return "cookieInfoView";
}

请求响应相关

java
// 将返回值直接写入响应体(代替视图渲染)
@ResponseBody
public User getUser()
java
// 组合注解 = @Controller + @ResponseBody
@RestController
public class ApiController
java
// 设置 HTTP 响应状态码
@ResponseStatus(HttpStatus.CREATED)
public void create()
java
// 	全局响应处理器 = @ControllerAdvice + @ResponseBody
@RestControllerAdvice
public class GlobalApiExceptionHandler {
    @ExceptionHandler(ValidationException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ErrorResponse handleValidationError(ValidationException ex) {
        return new ErrorResponse("VALIDATION_FAILED", ex.getErrors());
    }
}

全局异常处理器

@ControllerAdvice 拦截所有 @Controller ,常用来进行全局异常统一处理

java
@ControllerAdvice
public class GlobalControllerAdvice {
    // 全局异常处理器
    @ExceptionHandler(ResourceNotFoundException.class)
    public ResponseEntity<String> handleNotFound(ResourceNotFoundException ex) {
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ex.getMessage());
    }
    
}

跨域支持

@CrossOrigin 标注在类或方法上,启用跨域请求支持,不过一般都是使用全局跨域配置支持。