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/Response | Request/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 的请求处理流程?
答案:
- 请求到达 DispatcherServlet
- DispatcherServlet 通过 HandlerMapping 找到对应的 Handler
- 通过 HandlerAdapter 调用 Handler(Controller 方法)
- 执行前后会经过 Interceptor 的 preHandle/postHandle
- Handler 返回结果,如果是 @ResponseBody 直接序列化为 JSON
- 如果是视图,通过 ViewResolver 解析渲染
- 异常通过 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 状态码
异常处理按精确匹配优先,找不到时向上查找父类异常的处理器。
相关链接
- Spring MVC 官方文档
- IoC 容器与依赖注入 - Controller 也是 Spring Bean
- AOP 面向切面编程 - 拦截器与 AOP 的关系