我正在编写部署在 Tomcat 上的 Spring MVC 应用程序。请参阅以下 最小、完整且可验证的示例
public class Application extends AbstractAnnotationConfigDispatcherServletInitializer {
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] { };
}
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] { SpringServletConfig.class };
}
protected String[] getServletMappings() {
return new String[] { "/*" };
}
}
其中 SpringServletConfig
是
@Configuration
@ComponentScan("com.example.controllers")
@EnableWebMvc
public class SpringServletConfig {
@Bean
public InternalResourceViewResolver resolver() {
InternalResourceViewResolver vr = new InternalResourceViewResolver();
vr.setPrefix("/WEB-INF/jsps/");
vr.setSuffix(".jsp");
return vr;
}
}
最后,我在包中有一个 @Controller
com.example.controllers
@Controller
public class ExampleController {
@RequestMapping(path = "/home", method = RequestMethod.GET)
public String example() {
return "index";
}
}
我的应用程序的上下文名称是 Example
。当我向
http://localhost:8080/Example/home
应用程序响应 HTTP 状态 404 并记录以下内容
WARN o.s.web.servlet.PageNotFound - No mapping found for HTTP request with URI `[/Example/WEB-INF/jsps/index.jsp]` in `DispatcherServlet` with name 'dispatcher'
我在 /WEB-INF/jsps/index.jsp
有一个 JSP 资源 — 我希望 Spring MVC 使用我的控制器来处理请求并转发到 JSP,那么为什么它以 404 响应?
这是关于此警告消息的问题的规范帖子。
原文由 Sotirios Delimanolis 发布,翻译遵循 CC BY-SA 4.0 许可协议
您的标准 Spring MVC 应用程序将通过
DispatcherServlet
为所有请求提供服务,您已在 Servlet 容器中注册。The
DispatcherServlet
looks at itsApplicationContext
and, if available, theApplicationContext
registered with aContextLoaderListener
for special beans it needs to setup its request服务逻辑。 这些 bean 在文档中进行了描述。可以说是最重要的,类型为
HandlerMapping
map 的 beansHandlerMapping
的 javadoc 进一步描述了实现必须如何表现。DispatcherServlet
找到所有这种类型的 beans 并按某种顺序(可以自定义)注册它们。在服务请求时,DispatcherServlet
循环遍历这些HandlerMapping
对象并使用getHandler
以找到一个可以处理传入请求的代表标准HttpServletRequest
。从 4.3.x 开始, 如果找不到任何,它会 记录您看到的警告并抛出
NoHandlerFoundException
或 立即提交带有 404 Not Found 状态代码的响应。为什么
DispatcherServlet
找不到可以处理我的请求的HandlerMapping
?The most common
HandlerMapping
implementation isRequestMappingHandlerMapping
, which handles registering@Controller
beans as handlers (really their@RequestMapping
annotated methods).您可以自己声明这种类型的 bean(使用@Bean
或<bean>
或其他机制),也可以使用 内置选项。这些是:@EnableWebMvc
注释你的@Configuration
类。<mvc:annotation-driven />
成员。正如上面的链接所描述的,这两个都将注册一个
RequestMappingHandlerMapping
bean(和一堆其他东西)。但是,如果没有处理程序,HandlerMapping
不是很有用。RequestMappingHandlerMapping
expects some@Controller
beans so you need to declare those too, through@Bean
methods in a Java configuration or<bean>
declarations in an XML配置或通过组件扫描@Controller
中的注释类。 确保存在这些豆子。如果您收到警告消息和 404 并且您已正确配置以上所有内容, 那么您将请求发送到错误的 URI ,该 URI 未被检测到的
@RequestMapping
带注释的处理程序方法。spring-webmvc
库提供其他内置HandlerMapping
实现。例如,BeanNameUrlHandlerMapping
地图你可以随时编写自己的。显然, 您必须确保您发送的请求至少匹配一个已注册的
HandlerMapping
对象的处理程序。If you don’t implicitly or explicitly register any
HandlerMapping
beans (or ifdetectAllHandlerMappings
istrue
), theDispatcherServlet
registers some defaults .这些定义在DispatcherServlet.properties
与DispatcherServlet
类相同的包中。它们是BeanNameUrlHandlerMapping
和DefaultAnnotationHandlerMapping
(类似于RequestMappingHandlerMapping
但已弃用)。调试
Spring MVC 将记录通过
RequestMappingHandlerMapping
注册的处理程序。例如,一个@Controller
像将在 INFO 级别记录以下内容
这描述了注册的映射。当您看到没有找到处理程序的警告时,将消息中的 URI 与此处列出的映射进行比较。
@RequestMapping
中指定的所有限制必须与 Spring MVC 匹配才能选择处理程序。其他
HandlerMapping
实现记录它们自己的语句,这些语句应该提示它们的映射和相应的处理程序。同样,在 DEBUG 级别启用 Spring 日志记录以查看 Spring 注册了哪些 bean。它应该报告它找到了哪些带注释的类、它扫描了哪些包以及它初始化了哪些 bean。如果您期望的那些不存在,请检查您的
ApplicationContext
配置。其他常见错误
A
DispatcherServlet
只是一个典型的 Java EEServlet
。 You register it with your typical<web.xml>
<servlet-class>
and<servlet-mapping>
declaration, or directly throughServletContext#addServlet
in aWebApplicationInitializer
,或者使用 Spring Boot 使用的任何机制。因此,您必须依赖 Servlet 规范 中指定的 url 映射 逻辑,请参阅第 12 章。另请参阅考虑到这一点,一个常见的错误是使用
DispatcherServlet
/*
,从@RequestMapping
b0f 返回视图名称,并期待处理程序方法要呈现的 JSP。例如,考虑一个处理程序方法与
InternalResourceViewResolver
您可能希望将请求 转发 到路径
/WEB-INF/jsps/example-view-name.jsp
中的 JSP 资源。这不会发生。相反,假设上下文名称为Example
,DisaptcherServlet
将报告Because the
DispatcherServlet
is mapped to/*
and/*
matches everything (except exact matches, which have higher priority), theDispatcherServlet
would be选择处理来自forward
的JstlView
(由InternalResourceViewResolver
返回)。 在几乎所有情况下,DispatcherServlet
都不会被配置为处理此类请求。相反,在这种简单的情况下,您应该将
DispatcherServlet
注册到/
,将其标记为默认 servlet。默认 servlet 是请求的最后一个匹配项。这将允许您的典型 servlet 容器选择一个内部 Servlet 实现,映射到*.jsp
,以处理 JSP 资源(例如,Tomcat 有JspServlet
),然后再尝试使用默认的 servlet .这就是您在示例中看到的内容。