Spring MVC 核心架构
核心组件
组件 | 作用 | 类比 |
---|---|---|
DispatcherServlet | 前端控制器,统一接收请求并协调各组件处理 | 一个餐厅的前台 |
HandlerMapping | 根据请求URL映射到对应的处理器(Controller) | 路由表 |
HandlerAdapter | 执行处理器方法,处理参数绑定、方法调用等 | 适配器(兼容不同处理器) |
ViewResolver | 将逻辑视图名解析为物理视图(如JSP、Thymeleaf) | 地图导航 |
View | 渲染模型数据生成最终响应(HTML/JSON等) | 厨师(加工数据) |
HandlerExceptionResolver | 统一处理控制器抛出的异常 | 故障应急小组 |
分层架构
客户端 => DispatcherServlet => HandlerMapping
=> Controller => ModelAndView => View => 响应
相当于
客户端 => 前端控制器 => 处理器映射器 => 处理器 =>模型与视图容器 => 响应器 =>响应
请求处理的流程
- 请求到达:客户端发送http请求到前端控制器DispatcherServelt
- 查找处理器:前端控制器DispatcherServlet调用HandlerMapping确定目标控制器Controller
- 获取适配器:通过处理器适配器HandlerAdapter执行控制器方法
- 拦截器预处理:执行拦截器链的preHandle()
- 参数绑定:适配器解析请求参数(路径变量、表单数据等),传递给控制器方法
- 执行控制器:调用控制器Controller方法执行业务逻辑
- 返回模型与视图:控制器Controller返回ModelAndView(或String视图名、@ResponseBody等)
- 拦截器后处理:执行拦截器链后的postHandle()
- 视图解析:视图解析器ViewResolver将逻辑视图名转换为具体View对象
- 视图渲染:响应器View渲染模型数据产生响应内容(HTML/JSON)
- 拦截器完成:执行拦截器链的afterCompletion()
关键环节:HTTP请求 → Java对象
当请求到达控制器方法前,Spring MVC会自动提取请求中的各种数据
@RequestParam
:获取URL查询字符串参数
java
// 请求:/search?keyword=spring
@GetMapping("/search")
public String search(@RequestParam("keyword") String keyword) {
// keyword = "spring"
}
@PathVariable
:获取URL路径中的变量
java
// 请求:/users/123/profile
@GetMapping("/users/{userId}/profile")
public String profile(@PathVariable("userId") Long id) {
// id = 123
}
@RequestBody
:将JSON/XML请求体转换为Java对象
java
// 请求体:{"name":"John","age":30}
@PostMapping("/users")
public User createUser(@RequestBody User user) {
// user.getName() = "John"
// user.getAge() = 30
}
表单绑定:自动将表单字段映射到对象属性
java
// 表单字段:username=admin&password=123
@PostMapping("/login")
public String login(UserForm form) {
// form.getUsername() = "admin"
}
配置视图解析器的流程
(假如控制器返回一个字符串"success")
控制器返回字符串 => return 'success'=> 视图解析器=> 检查配置
=> 前缀+字符串success+后缀=> 合成地址/WEB-INF/views/success.jsp=> 返回实际视图文件
应用配置的前缀和后缀:
java
resolver.setPrefix("/WEB-INF/views/"); // 视图文件目录
resolver.setSuffix(".jsp"); // 文件扩展名
配置文件示例:
java
@Configuration
public class WebConfig {
@Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/"); // 视图存放目录
resolver.setSuffix(".jsp"); // 文件扩展名
return resolver;
}
}
异常的分层处理
方法级处理(优先级最高)
java
@Controller
public class UserController {
@ExceptionHandler(UserNotFoundException.class)
public ResponseEntity<String> handleUserNotFound() {
return ResponseEntity.status(404).body("用户不存在");
}
}
控制器级处理
java
@Controller
@ExceptionHandler({IllegalArgumentException.class,
DataAccessException.class})
public ResponseEntity<String> handleControllerExceptions() {
// 处理本控制器所有指定异常
}
全局处理(最常用)
java
@ControllerAdvice // 作用于所有控制器
public class GlobalExceptionHandler {
// 处理特定异常
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ErrorResponse> handleResourceNotFound(
ResourceNotFoundException ex) {
ErrorResponse error = new ErrorResponse(
"NOT_FOUND",
ex.getMessage(),
System.currentTimeMillis()
);
return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
}
// 兜底处理所有未捕获异常
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleAllExceptions(Exception ex) {
// 返回通用错误响应
}
}
关于用户访问个人资料页面的示例
请求 :GET /users/123/profile
参数绑定 :提取路径变量 userId=123
控制器方法处理:
java
@GetMapping("/users/{userId}/profile")
public String userProfile(@PathVariable Long userId, Model model) {
User user = userService.findById(userId); // 可能抛出异常
model.addAttribute("user", user);
return "user/profile"; // 逻辑视图名
}
处理异常: 若用户不存在,抛出UserNotFoundException
视图解析 :将"user/profile"
转换为/WEB-INF/views/user/profile.jsp
渲染页面并返回响应