跳到主要内容

Spring MVC

问题

Spring MVC 的请求处理流程是怎样的?DispatcherServlet 的作用是什么?拦截器和过滤器有什么区别?

答案

请求处理流程

核心组件

组件职责
DispatcherServlet前端控制器,请求入口,协调各组件
HandlerMapping根据请求 URL 找到对应的 Handler(Controller 方法)
HandlerAdapter调用 Handler 并处理参数绑定、返回值解析
HandlerInterceptor拦截器,在 Handler 前后执行
ViewResolver视图解析器(前后端分离项目较少使用)
HandlerExceptionResolver统一异常处理

常用注解

ControllerAnnotations.java
@RestController // = @Controller + @ResponseBody
@RequestMapping("/api/users")
public class UserController {

@GetMapping("/{id}")
public User getUser(@PathVariable Long id) { /* ... */ }

@PostMapping
public User createUser(@RequestBody @Valid UserDTO dto) { /* ... */ }

@PutMapping("/{id}")
public User updateUser(@PathVariable Long id, @RequestBody UserDTO dto) { /* ... */ }

@DeleteMapping("/{id}")
public void deleteUser(@PathVariable Long id) { /* ... */ }

@GetMapping
public List<User> listUsers(
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "10") int size) { /* ... */ }
}

拦截器(HandlerInterceptor)

AuthInterceptor.java
@Component
public class AuthInterceptor implements HandlerInterceptor {

// Handler 执行前(返回 false 则中断请求)
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) {
String token = request.getHeader("Authorization");
if (token == null) {
response.setStatus(401);
return false; // 中断请求
}
return true; // 继续执行
}

// Handler 执行后、视图渲染前
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView 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 AuthInterceptor())
.addPathPatterns("/api/**")
.excludePathPatterns("/api/public/**");
}
}

过滤器 vs 拦截器

对比Filter(过滤器)HandlerInterceptor(拦截器)
规范Servlet 规范Spring MVC 规范
作用范围所有请求(包括静态资源)只拦截 Handler 请求
可访问内容Request/ResponseRequest/Response + Handler 信息
Spring Bean不方便注入可以注入 Spring Bean
执行时机DispatcherServlet 之前DispatcherServlet 之后
异常处理无法使用 Spring 异常处理可以使用 @ExceptionHandler

统一异常处理

GlobalExceptionHandler.java
@RestControllerAdvice
public class GlobalExceptionHandler {

// 处理参数校验异常
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ErrorResponse handleValidation(MethodArgumentNotValidException e) {
String message = e.getBindingResult().getFieldErrors().stream()
.map(error -> error.getField() + ": " + error.getDefaultMessage())
.collect(Collectors.joining(", "));
return new ErrorResponse(400, message);
}

// 处理业务异常
@ExceptionHandler(BusinessException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ErrorResponse handleBusiness(BusinessException e) {
return new ErrorResponse(e.getCode(), e.getMessage());
}

// 兜底处理
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ErrorResponse handleException(Exception e) {
log.error("系统异常", e);
return new ErrorResponse(500, "系统繁忙,请稍后重试");
}
}

常见面试问题

Q1: Spring MVC 的请求处理流程?

答案

  1. 请求到达 DispatcherServlet
  2. DispatcherServlet 通过 HandlerMapping 找到对应的 Handler
  3. 通过 HandlerAdapter 调用 Handler(Controller 方法)
  4. 执行前后会经过 Interceptor 的 preHandle/postHandle
  5. Handler 返回结果,如果是 @ResponseBody 直接序列化为 JSON
  6. 如果是视图,通过 ViewResolver 解析渲染
  7. 异常通过 HandlerExceptionResolver 统一处理

Q2: @Controller 和 @RestController 的区别?

答案

@RestController = @Controller + @ResponseBody

  • @Controller:方法返回值默认是视图名称(需要搭配 @ResponseBody 返回 JSON)
  • @RestController:方法返回值直接序列化为 JSON/XML 响应体

前后端分离项目统一使用 @RestController

Q3: 拦截器和过滤器的区别?

答案

Filter 属于 Servlet 规范,在 DispatcherServlet 之前执行,对所有请求生效。Interceptor 属于 Spring MVC,在 DispatcherServlet 之后执行,只拦截 Handler 请求,可以获取 Handler 信息和注入 Spring Bean。

一般认证/鉴权用拦截器,字符编码/CORS 用过滤器。

Q4: @RequestBody 和 @RequestParam 的区别?

答案

  • @RequestBody:从请求体中读取 JSON/XML,反序列化为对象(POST/PUT)
  • @RequestParam:从 URL 查询参数或表单参数中获取值(GET/POST 表单)
  • @PathVariable:从 URL 路径中获取值(如 /users/{id}

Q5: 如何实现全局异常处理?

答案

使用 @RestControllerAdvice + @ExceptionHandler

  • @RestControllerAdvice:全局的 Controller 增强
  • @ExceptionHandler(XxxException.class):处理特定类型的异常
  • @ResponseStatus:指定 HTTP 状态码

异常处理按精确匹配优先,找不到时向上查找父类异常的处理器。

相关链接