您的标准 Spring MVC 应用程序将通过您在 Servlet 容器中注册的 DispatcherServlet 处理所有请求。
DispatcherServlet 查看它的ApplicationContext,如果可用,ApplicationContext 注册了一个ContextLoaderListener,用于设置其请求服务逻辑所需的特殊bean。 These beans are described in the documentation.
可以说是最重要的,HandlerMapping map 类型的 bean
对处理程序的传入请求以及前处理程序和后处理程序的列表
(处理程序拦截器)基于一些标准,其中的细节
因HandlerMapping 实施而异。最流行的实现
支持带注释的控制器,但其他实现存在为
好吧。
javadoc of HandlerMapping 进一步描述了实现必须如何表现。
DispatcherServlet 查找所有这种类型的 bean 并以某种顺序注册它们(可以自定义)。在处理请求时,DispatcherServlet 循环遍历这些 HandlerMapping 对象并使用 getHandler 测试每个对象,以找到可以处理传入请求的对象,表示为标准 HttpServletRequest。从 4.3.x 开始,如果没有找到,您会看到 logs the warning
在名称为 SomeName 的 DispatcherServlet 中找不到具有 URI [/some/path] 的 HTTP 请求的映射
并且either 抛出NoHandlerFoundException 或立即提交带有 404 Not Found 状态代码的响应。
为什么DispatcherServlet 没有找到可以处理我的请求的HandlerMapping?
最常见的HandlerMapping 实现是RequestMappingHandlerMapping,它将@Controller bean 注册为处理程序(实际上是它们的@RequestMapping 注释方法)。您可以自己声明这种类型的 bean(使用 @Bean 或 <bean> 或其他机制),也可以使用 the built-in options。它们是:
- 使用
@EnableWebMvc 注释您的 @Configuration 类。
- 在您的 XML 配置中声明一个
<mvc:annotation-driven /> 成员。
正如上面的链接所描述的,这两个都将注册一个RequestMappingHandlerMapping bean(和一堆其他的东西)。但是,如果没有处理程序,HandlerMapping 并不是很有用。 RequestMappingHandlerMapping 需要一些 @Controller bean,因此您也需要通过 Java 配置中的 @Bean 方法或 XML 配置中的 <bean> 声明或通过对其中 @Controller 注释类的组件扫描来声明它们。 确保这些 bean 存在。
如果您收到警告消息和 404 并且您已正确配置上述所有内容,那么您将请求发送到错误的 URI,一个未处理的通过检测到的@RequestMapping 注释处理程序方法。
spring-webmvc 库提供了其他内置的 HandlerMapping 实现。例如,BeanNameUrlHandlerMapping 地图
从 URL 到名称以斜杠 ("/") 开头的 bean
而且你总是可以自己写。显然,您必须确保您发送的请求至少与注册的HandlerMapping 对象的处理程序之一匹配。
如果您没有隐式或显式注册任何HandlerMapping bean(或者如果detectAllHandlerMappings 是true),则DispatcherServlet 会注册一些defaults。这些定义在与DispatcherServlet 类相同的包中的DispatcherServlet.properties 中。它们是BeanNameUrlHandlerMapping 和DefaultAnnotationHandlerMapping(类似于RequestMappingHandlerMapping,但已弃用)。
调试
Spring MVC 将记录通过RequestMappingHandlerMapping 注册的处理程序。例如,@Controller 喜欢
@Controller
public class ExampleController {
@RequestMapping(path = "/example", method = RequestMethod.GET, headers = "X-Custom")
public String example() {
return "example-view-name";
}
}
将在 INFO 级别记录以下内容
Mapped "{[/example],methods=[GET],headers=[X-Custom]}" onto public java.lang.String com.spring.servlet.ExampleController.example()
这描述了注册的映射。当您看到未找到处理程序的警告时,将消息中的 URI 与此处列出的映射进行比较。 @RequestMapping 中指定的所有限制都必须匹配 Spring MVC 才能选择处理程序。
其他HandlerMapping 实现会记录它们自己的语句,这些语句应该提示它们的映射和对应的处理程序。
同样,在 DEBUG 级别启用 Spring 日志记录以查看 Spring 注册了哪些 bean。它应该报告它找到了哪些带注释的类,它扫描了哪些包,以及它初始化了哪些 bean。如果您预期的不存在,请检查您的 ApplicationContext 配置。
其他常见错误
DispatcherServlet 只是一个典型的 Java EE Servlet。您可以使用典型的 <web.xml> <servlet-class> 和 <servlet-mapping> 声明注册它,或者直接通过 ServletContext#addServlet 在 WebApplicationInitializer 中注册它,或者使用 Spring boot 使用的任何机制。因此,您必须依赖Servlet specification 中指定的 url 映射 逻辑,请参阅第 12 章。另请参阅
考虑到这一点,一个常见的错误是使用/* 的url 映射注册DispatcherServlet,从@RequestMapping 处理程序方法返回视图名称,并期望呈现一个JSP。例如,考虑一个处理方法,如
@RequestMapping(path = "/example", method = RequestMethod.GET)
public String example() {
return "example-view-name";
}
InternalResourceViewResolver
@Bean
public InternalResourceViewResolver resolver() {
InternalResourceViewResolver vr = new InternalResourceViewResolver();
vr.setPrefix("/WEB-INF/jsps/");
vr.setSuffix(".jsp");
return vr;
}
您可能期望forwarded 对路径/WEB-INF/jsps/example-view-name.jsp 处的JSP 资源的请求。这不会发生。相反,假设上下文名称为 Example,DisaptcherServlet 将报告
在名称为“dispatcher”的DispatcherServlet 中找不到具有URI [/Example/WEB-INF/jsps/example-view-name.jsp] 的HTTP 请求的映射
因为DispatcherServlet 被映射到/* 并且/* 匹配所有内容(除了具有更高优先级的完全匹配),将选择DispatcherServlet 来处理来自JstlView 的forward(由InternalResourceViewResolver 返回)。 几乎在所有情况下,DispatcherServlet 都不会配置为处理此类请求。
相反,在这种简单的情况下,您应该将DispatcherServlet 注册到/,将其标记为默认servlet。默认 servlet 是请求的最后一个匹配项。这将允许您的典型 servlet 容器在尝试使用默认 servlet 之前选择一个映射到 *.jsp 的内部 Servlet 实现来处理 JSP 资源(例如,Tomcat 有 JspServlet)。
这就是您在示例中看到的内容。