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查詢字符串參數
// 請求:/search?keyword=spring
@GetMapping("/search")
public String search(@RequestParam("keyword") String keyword) {// keyword = "spring"
}
@PathVariable
:獲取URL路徑中的變量
// 請求:/users/123/profile
@GetMapping("/users/{userId}/profile")
public String profile(@PathVariable("userId") Long id) {// id = 123
}
@RequestBody
:將JSON/XML請求體轉換為Java對象
// 請求體:{"name":"John","age":30}
@PostMapping("/users")
public User createUser(@RequestBody User user) {// user.getName() = "John"// user.getAge() = 30
}
表單綁定:自動將表單字段映射到對象屬性
// 表單字段:username=admin&password=123
@PostMapping("/login")
public String login(UserForm form) {// form.getUsername() = "admin"
}
配置視圖解析器的流程
(假如控制器返回一個字符串“success”)
控制器返回字符串?=> return ‘success’=> 視圖解析器=> 檢查配置
=> 前綴+字符串success+后綴=> 合成地址/WEB-INF/views/success.jsp=> 范文實際視圖文件
應用配置的前綴和后綴:
resolver.setPrefix("/WEB-INF/views/"); // 視圖文件目錄
resolver.setSuffix(".jsp"); // 文件擴展名
配置文件示例:
@Configuration
public class WebConfig {@Beanpublic ViewResolver viewResolver() {InternalResourceViewResolver resolver = new InternalResourceViewResolver();resolver.setPrefix("/WEB-INF/views/"); // 視圖存放目錄resolver.setSuffix(".jsp"); // 文件擴展名return resolver;}
}
異常的分層處理
方法級處理(優先級最高)
@Controller
public class UserController {@ExceptionHandler(UserNotFoundException.class)public ResponseEntity<String> handleUserNotFound() {return ResponseEntity.status(404).body("用戶不存在");}
}
控制器級處理
@Controller
@ExceptionHandler({IllegalArgumentException.class, DataAccessException.class})
public ResponseEntity<String> handleControllerExceptions() {// 處理本控制器所有指定異常
}
全局處理(最常用)
@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
控制器方法處理:
@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
渲染頁面并返回響應