SPRING-HIBERNATE SERIES - BÀI 0.2
Hai List, Một Luật Chọn Đầu Tiên
LIST CÓ ĐÁY - KHUÔN MẶT SỐ 1
Đọc từ trái sang phải. Màu đậm dần nghĩa là gần cơ chế hơn. Dấu
* đánh dấu điểm bài viết khai thác.
Triệu chứng A - thiếu @RequestBody
UserForm(name=null, email=null)
Cùng một gốcList chọn phần tử đầu tiên báo hỗ trợ; fallback hợp lệ che mất lời khai bị thiếu.
Triệu chứng B - thiếu @ResponseBody
return "ok" bị hiểu là tên view
Mức 1 - luồng sync phổ biến
DispatcherServlet.doDispatch()
Đường controller có annotation, không chạy async
checkMultipart()Bọc request multipart nếu cầngetHandler()TìmHandlerExecutionChaingetHandlerAdapter()Chọn adapter đầu tiên hỗ trợ handlerapplyPreHandle()Interceptor có thể dừng requestha.handle()Đi vàoRequestMappingHandlerAdapterapplyDefaultViewName()Bổ sung view mặc định khi cầnapplyPostHandle()Interceptor xử lý sau handlerprocessDispatchResult()Giải exception, render và hoàn tấtcleanupMultipart()Dọn tài nguyên ở nhánh đồng bộ
Mức 2 - zoom ha.handle()
RequestMappingHandlerAdapter
Chuẩn bị, gọi method, rồi diễn giải giá trị trả về
handleInternal()Kiểm tra request và session policyinvokeHandlerMethod()Dựng ngữ cảnh gọi controllergetDataBinderFactory()Chuẩn bị binding và conversiongetModelFactory()Khởi tạo model và session attributescreateInvocableHandlerMethod()Gắn argument resolvers và return handlersmodelFactory.initModel()Nạp model trước khi gọi controllergetMethodArgumentValues()List argument resolver chọn người xử lý - mở ra nhánh A bên phảidoInvoke()Gọi method với arguments đã đúchandleReturnValue()List return handler diễn giải kết quả - mở ra nhánh B bên phảigetModelAndView()Trả kết quả về DispatcherServlet
Mức 3 - zoom 2 điểm chọn
First match wins, rồi được cache
Trích đúng đoạn thứ tự quyết định 2 triệu chứng
A. Argument resolver
HandlerMethodArgumentResolverComposite.getArgumentResolver()
RequestParamMethodArgumentResolver(false)Chỉ nhận lời khai phù hợpServletModelAttributeMethodProcessor(false)Nhận@ModelAttributeRequestResponseBodyMethodProcessorChỉ nhận@RequestBodyvà đọc body- Các resolver annotation và type khácSau đó mới tới custom resolvers
PrincipalMethodArgumentResolverBắt đầu nhóm catch-allRequestParamMethodArgumentResolver(true)Nhận kiểu đơn giản không annotationServletModelAttributeMethodProcessor(true)Nhận object phức tạp, bind request parameters; JSON body không được converter đọc
B. Return value handler
HandlerMethodReturnValueHandlerComposite.selectHandler()
- Các handler kiểu trả về chuyên biệt
ModelAndView,ResponseEntity, async và streaming ServletModelAttributeMethodProcessor(false)Nhận@ModelAttributeRequestResponseBodyMethodProcessorNhận@ResponseBodyhoặc@RestControllerViewNameMethodReturnValueHandlerStringkhông lời khai trở thành view nameMapMethodProcessorvà custom handlersChạy trước catch-all cuốiServletModelAttributeMethodProcessor(true)Fallback cuối nếu không có ModelAndViewResolver
Kiểm chứng đúng lúc
Đặt breakpoint tại getArgumentResolver(), rồi restart app hoặc gọi endpoint chưa chạy. Sau lần chọn đầu, resolver được lưu trong argumentResolverCache; request sau thường không đi qua vòng for.
Lối ra theo đúng hợp đồng
- JSON requestThêm
@RequestBodyđể dừng tại body processor. - REST responseDùng
@ResponseBodyhoặc@RestControllerđể ghi response body.
Khi KHÔNG được kết luận
- Object nhận vào rỗngChưa đủ để kết luận thiếu annotation; body rỗng, key sai hoặc binder tùy biến cũng có thể gây triệu chứng.
- String bị tìm như viewChưa đủ để kết luận controller thiếu annotation; method có thể chủ ý trả view, còn view resolver vẫn cần được kiểm tra.