Ứng dụng Spring MVC tiêu chuẩn của bạn sẽ phục vụ tất cả các yêu cầu thông qua một DispatcherServletmà bạn đã đăng ký với vùng chứa Servlet của mình.
Cái DispatcherServletnhìn vào nó ApplicationContextvà, nếu có, được ApplicationContextđăng ký với một ContextLoaderListenerhạt đậu đặc biệt, nó cần thiết lập logic phục vụ yêu cầu của nó. Những hạt đậu này được mô tả trong tài liệu .
Được cho là quan trọng nhất, đậu của loại HandlerMappingbản đồ
các yêu cầu đến trình xử lý và danh sách các trình xử lý trước và sau (trình đánh chặn trình xử lý) dựa trên một số tiêu chí, chi tiết của chúng khác nhau tùy theo cách HandlerMappingtriển khai. Việc triển khai phổ biến nhất hỗ trợ các bộ điều khiển được chú thích nhưng các triển khai khác cũng tồn tại.
Các javadoc củaHandlerMapping thêm mô tả cách triển khai phải cư xử.
Tìm DispatcherServletthấy tất cả các loại đậu thuộc loại này và đăng ký chúng theo một số thứ tự (có thể được tùy chỉnh). Trong khi phục vụ một yêu cầu, các DispatcherServletvòng lặp qua các HandlerMappingđối tượng này và kiểm tra từng đối tượng getHandlerđể tìm một đối tượng có thể xử lý yêu cầu đến, được biểu thị dưới dạng tiêu chuẩn HttpServletRequest. Kể từ 4.3.x, nếu nó không tìm thấy bất kỳ , nó sẽ ghi lại cảnh báo mà bạn thấy
Không có ánh xạ tìm thấy cho yêu cầu HTTP URI [/some/path]trong DispatcherServletvới tên somename
và một trong hai ném một NoHandlerFoundExceptionhoặc ngay lập tức cam kết đáp ứng với một mã trạng thái 404 Not Found.
Tại sao không DispatcherServlettìm thấy một HandlerMappingcó thể xử lý yêu cầu của tôi?
Cách HandlerMappingtriển khai phổ biến nhất là RequestMappingHandlerMappingxử lý việc đăng ký @Controllerbean dưới dạng trình xử lý (thực sự là các @RequestMappingphương thức được chú thích của chúng ). Bạn có thể tự khai báo bean loại này (với @Beanhoặc <bean>hoặc cơ chế khác) hoặc bạn có thể sử dụng các tùy chọn tích hợp sẵn . Đó là:
- Chú thích
@Configurationlớp của bạn với @EnableWebMvc.
- Khai báo một
<mvc:annotation-driven />thành viên trong cấu hình XML của bạn.
Như liên kết ở trên mô tả, cả hai thứ này sẽ đăng ký một RequestMappingHandlerMappingbean (và một loạt các thứ khác). Tuy nhiên, một HandlerMappingkhông hữu ích lắm nếu không có một trình xử lý. RequestMappingHandlerMappingmong đợi một số @Controllerbean, vì vậy bạn cũng cần phải khai báo chúng, thông qua @Beancác phương thức trong cấu hình Java hoặc <bean>khai báo trong cấu hình XML hoặc thông qua quét thành phần của @Controllercác lớp được chú thích trong một trong hai. Đảm bảo có những hạt đậu này.
Nếu bạn nhận được thông báo cảnh báo và mã 404 và bạn đã định cấu hình tất cả các điều trên một cách chính xác, thì bạn đang gửi yêu cầu của mình đến sai URI , một URI không được xử lý bằng @RequestMappingphương pháp xử lý có chú thích được phát hiện .
Các spring-webmvcMời thư viện khác được xây dựng trong HandlerMappingviệc triển khai. Ví dụ, BeanNameUrlHandlerMappingbản đồ
từ URL đến bean có tên bắt đầu bằng dấu gạch chéo ("/")
và bạn luôn có thể viết của riêng bạn. Rõ ràng, bạn sẽ phải đảm bảo rằng yêu cầu bạn đang gửi khớp với ít nhất một trong các HandlerMappingtrình xử lý của đối tượng đã đăng ký .
Nếu bạn không đăng ký ngầm định hoặc rõ ràng bất kỳ HandlerMappingbean nào (hoặc nếu detectAllHandlerMappingslà true), thì DispatcherServletđăng ký một số giá trị mặc định . Chúng được định nghĩa trong DispatcherServlet.propertiescùng một gói với DispatcherServletlớp. Chúng là BeanNameUrlHandlerMappingvà DefaultAnnotationHandlerMapping(tương tự như RequestMappingHandlerMappingnhưng không được dùng nữa).
Gỡ lỗi
Spring MVC sẽ ghi lại các trình xử lý đã đăng ký thông qua RequestMappingHandlerMapping. Ví dụ, một @Controllerlike
@Controller
public class ExampleController {
@RequestMapping(path = "/example", method = RequestMethod.GET, headers = "X-Custom")
public String example() {
return "example-view-name";
}
}
sẽ ghi lại những điều sau ở cấp độ INFO
Mapped "{[/example],methods=[GET],headers=[X-Custom]}" onto public java.lang.String com.spring.servlet.ExampleController.example()
Điều này mô tả ánh xạ đã đăng ký. Khi bạn thấy cảnh báo rằng không tìm thấy trình xử lý nào, hãy so sánh URI trong thông báo với ánh xạ được liệt kê ở đây. Tất cả các giới hạn được chỉ định trong @RequestMappingphải khớp với Spring MVC để chọn trình xử lý.
Các HandlerMappingtriển khai khác ghi lại các câu lệnh của riêng chúng mà sẽ gợi ý cho các ánh xạ của chúng và các trình xử lý tương ứng của chúng.
Tương tự, hãy kích hoạt tính năng ghi nhật ký Spring ở mức GỠ LỖI để xem Spring đăng ký đậu nào. Nó sẽ báo cáo lớp được chú thích nào mà nó tìm thấy, gói nào nó quét và đậu nó khởi tạo. Nếu những cái bạn mong đợi không có, hãy xem lại ApplicationContextcấu hình của bạn .
Những lỗi phổ biến khác
A DispatcherServletchỉ là một Java EE điển hình Servlet. Bạn đăng ký nó với thông thường <web.xml> <servlet-class>và <servlet-mapping>khai báo của bạn , hoặc trực tiếp thông qua ServletContext#addServletmột WebApplicationInitializerhoặc với bất kỳ cơ chế nào mà Spring boot sử dụng. Như vậy, bạn phải dựa vào logic ánh xạ url được chỉ định trong đặc tả Servlet , xem Chương 12. Xem thêm
Với ý nghĩ đó, một sai lầm phổ biến là đăng ký DispatcherServletánh xạ url của /*, trả về tên chế độ xem từ @RequestMappingphương thức xử lý và mong đợi một JSP được hiển thị. Ví dụ: hãy xem xét một phương thức xử lý như
@RequestMapping(path = "/example", method = RequestMethod.GET)
public String example() {
return "example-view-name";
}
với một InternalResourceViewResolver
@Bean
public InternalResourceViewResolver resolver() {
InternalResourceViewResolver vr = new InternalResourceViewResolver();
vr.setPrefix("/WEB-INF/jsps/");
vr.setSuffix(".jsp");
return vr;
}
bạn có thể mong đợi yêu cầu được chuyển tiếp đến tài nguyên JSP tại đường dẫn /WEB-INF/jsps/example-view-name.jsp. Điều này sẽ không xảy ra. Thay vào đó, giả sử có tên ngữ cảnh là Example, DisaptcherServletsẽ báo cáo
Không có ánh xạ tìm thấy cho HTTP yêu cầu với URI [/Example/WEB-INF/jsps/example-view-name.jsp]trong DispatcherServletvới tên 'phối'
Bởi vì dấu DispatcherServletđược ánh xạ tới /*và /*khớp với mọi thứ (ngoại trừ các kết quả chính xác, có mức độ ưu tiên cao hơn), nên DispatcherServletsẽ được chọn để xử lý forwardtừ JstlView(được trả về bởi InternalResourceViewResolver). Trong hầu hết mọi trường hợp, DispatcherServletsẽ không được cấu hình để xử lý một yêu cầu như vậy .
Thay vào đó, trong trường hợp đơn giản này, bạn nên đăng ký DispatcherServlettới /, đánh dấu nó là servlet mặc định. Servlet mặc định là kết quả phù hợp cuối cùng cho một yêu cầu. Điều này sẽ cho phép vùng chứa servlet điển hình của bạn chọn một triển khai Servlet nội bộ, được ánh xạ tới *.jsp, để xử lý tài nguyên JSP (ví dụ: Tomcat có JspServlet), trước khi thử với servlet mặc định.
Đó là những gì bạn đang thấy trong ví dụ của mình.