Springboot 常用 Spring MVC 實現 web 服務。
Spring MVC 請求處理流程
圖片來自《Spring 實戰第四版》
瀏覽器請求首先被交給 DispatcherServlet 前端控制器。
DispatcherServlet 查詢處理器映射以決定將請求發送給哪個控制器。控制器處理業務邏輯后,向 DispatcherServlet 返回處理結果(被稱為模型)和邏輯視圖名。DispatcherServlet 使用視圖解析器為邏輯視圖名匹配特定視圖。最后視圖渲染結果,交給響應對象返回瀏覽器。
MVC 啟動
Springboot 啟動 Spring MVC 時,動態加載\org\springframework\web\servlet\DispatcherServlet.properties
文件定義的組件。
控制器
@Controller 注解定義控制器。它與 @Component 注解功能相同,因此Spring 會創建 MyController bean。
@RequestMapping 注解定義請求路徑(value 屬性)和控制器方法(hello())的映射關系。映射關系被存儲到HandlerMapping(處理器映射)對象。DispatcherServlet 將請求發送給處理器映射,處理器映射返回的不是 Controller,而是 HandlerExecutionChain 對象。
@Controller
public class MyController {@RequestMapping(value = "/", method=GET)public String hello() {return "hello";}
}
HandlerExecutionChain 對象包含處理器 handler 和攔截器 interceptorList 。處理器是對控制器 controller 的封裝。攔截器增強處理器功能。
public class HandlerExecutionChain {private final Object handler;private final List<HandlerInterceptor> interceptorList = new ArrayList<>();
HandlerAdapter 實現類執行 HandlerExecutionChain 對象的內容。即執行控制器并返回結果。
控制器返回的結果被稱為模型。視圖解析器根據邏輯視圖名稱定位視圖,視圖渲染模型給響應體。前后端分離場景下一般用 json 格式響應體,此時不需要視圖解析器和視圖。
@RequestMapping
@RequestMapping 注解有主要有兩個參數,一個是路徑 value,一個是請求方法 method。它的簡化版本 @GetMapping 注解表示 get 請求類型,@PostMapping 注解表示 Post 請求類型。
獲取控制器參數
@RequestParam 注解可以定義HTTP參數和方法參數的映射關系。
@RequestMapping(value = "/param", method=GET)
public String param(@RequestParam("user") String user,@RequestParam("age") int age) {return service(user, age);
}
@RequestBody 注解可以將 json 參數轉換為對應 java 對象。
@RequestMapping(value = "/param", method=GET)
public String param(@RequestBody User user) {return service(user);
}
@PathVariable 注解可以獲取路徑參數。Restful 風格要求用路徑,而不是用參數表示資源。
@RequestMapping(value = "/param/{id}", method=GET)
public String param(@PathVariable("id") String id) {return service(id);
}
@DataTimeFormat 注解和 @NumberFormat 注解可以定義格式化參數。
自定義獲取控制器參數的規則
SpringMVC 通過 WebDataBinder 機制來獲取參數。它解析 HTTP 請求上下文,轉換參數并且提供驗證功能。轉換參數的接口有三個:Converter,Formatter 和 GenericConverter。第一個是轉換類型,第二個是轉換格式,第三個是轉換數組。
SpringMVC 將三個接口的默認實現類注冊到注冊機,這就是大部分類型轉換無需開發者開發的原因。
我們可以定義自己的轉換器,只需實現接口,SpringMVC自動注冊到注冊機。
@Component
public class MyConvrter implements Converter<String, User> {@Overridepublic User convert(String str) {...}
}
參數驗證
SpringMVC 支持參數驗證。通過 @Valid 注解啟動驗證機制,通過 @NotNull, @Max, @Range, @Email 等注解驗證字段。
用戶也可以在 WebDataBinder 注冊驗證器自定義驗證機制。
public class MyValidator implements Validator {// 指定驗證類型@Overridepublic boolean supports(Class<?> clazz) {return clazz.equals(User.class);}// 執行驗證方法@Overridepublic void validate(Object target, Errors erros) {...}
}
MyValidator 類沒有 @Component 注解。在控制器類用 @InitBinder 注解注冊驗證類。
@RequestMapping("/user")
public class UserController {@InitBinderpublic void initBinder(WebDataBinder binder) {binder.setValidator(new MyValidator()); // 綁定驗證器}@GetMapping("/validate")public Map<String, Object> validator(@Valid User user, Errors erros) {if (errors.hasErrors()) {// 沒通過驗證,返回錯誤結果}}
}
視圖
SpringMVC 視圖分為邏輯視圖和非邏輯視圖。邏輯視圖需要視圖解析器進一步定位視圖,比如 JSP。非邏輯視圖不需定位,直接渲染,比如 json。
攔截器
攔截器用于攔截處理器,增強處理器功能。preHandle 方法在處理器執行前執行,postHandle 方法在處理器執行后執行,afterCompletion 方法在處理器完成且視圖渲染后后執行。
public class MyInterceptor extends HandlerInterceptor {@Overridepublic boolean preHandle(HttpServltRequest request, HttpServletResponse response, Object handler) {...}@Overridepublic boolean postHandle(HttpServltRequest request, HttpServletResponse response, Object handler) {...}@Overridepublic boolean afterCompletion(HttpServltRequest request, HttpServletResponse response, Object handler) {...}
}
開發攔截器后需要注冊攔截器。
@Configuration
public class MyApplication implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {InterceptorRegistry ir = registry.addInterceptor(new MyInterceptor());ir.addPathPatterns("/user/*");}
}
多個攔截器按照責任鏈模式裝配。對于 preHandle 方法,先注冊先執行。對于 postHandle 和 afterCompletion 方法,先注冊后執行。如果某個攔截器的 preHandle 為 false,后續的 preHandle 和 postHandle 都不會執行。