我正在编写一个部署在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'
我有一个JSP资源,/WEB-INF/jsps/index.jsp
我期望Spring MVC使用我的控制器来处理请求并转发到JSP,那么为什么它会响应404呢?
对于有关此警告消息的问题,这是一个规范的帖子.
您的标准Spring MVC应用程序将通过DispatcherServlet
您在Servlet容器中注册的所有请求提供服务.
将DispatcherServlet
在它的外观ApplicationContext
,如果可用,ApplicationContext
用注册ContextLoaderListener
的,它需要建立其请求的服务逻辑特殊豆.这些bean在文档中描述.
可以说是最重要的豆类型的HandlerMapping
地图
对处理程序的传入请求以及基于某些标准的预处理器和后处理器列表(处理程序拦截器),其详细信息因
HandlerMapping
实现而异.最流行的实现支持带注释的控制器,但也存在其他实现.
该的javadoc中HandlerMapping
进一步描述了实现必须的行为.
该DispatcherServlet
认为这一类型的所有豆类和以某种顺序将它们登记(可定制).在提供请求时,DispatcherServlet
循环遍历这些HandlerMapping
对象并测试每个对象,getHandler
以找到可以处理传入请求的对象,表示为标准HttpServletRequest
.从4.3.x开始,如果找不到,则会记录您看到的警告
没有找到映射与URI HTTP请求
[/some/path]
中DispatcherServlet
使用的名字SomeName
并且要么抛出一个NoHandlerFoundException
或立即提交一个404 Not Found状态码的响应.
DispatcherServlet
找不到HandlerMapping
可以处理我的请求?
最常见的HandlerMapping
实现是RequestMappingHandlerMapping
,它将@Controller
bean 注册为处理程序(实际上是它们的@RequestMapping
注释方法).您可以声明这种类型的豆你自己(与@Bean
或
或其他机制),也可以使用内置的选项.这些是:
用你的@Configuration
课程注释@EnableWebMvc
.
在XML配置中声明成员.
正如上面的链接所描述的,这两个都将注册一个RequestMappingHandlerMapping
bean(和一堆其他的东西).但是,HandlerMapping
没有处理程序,a 不是很有用.RequestMappingHandlerMapping
期望一些@Controller
bean,所以你需要通过@Bean
Java配置中的方法或
XML配置中的声明或通过组件扫描@Controller
任何一个中的注释类来声明它们.确保这些豆子存在.
如果您收到警告消息和404,并且您已正确配置了上述所有内容,则您将请求发送到错误的URI,该URI不会被检测到的带@RequestMapping
注释的处理程序方法处理.
该spring-webmvc
库提供其他内置HandlerMapping
实现.例如,BeanNameUrlHandlerMapping
地图
从URL到以名称以斜杠开头的bean("/")
而你总是可以自己写.显然,您必须确保您发送的请求至少与注册HandlerMapping
对象的一个处理程序匹配.
如果您没有隐式或显式注册任何HandlerMapping
bean(或者如果detectAllHandlerMappings
是true
),则DispatcherServlet
寄存器会默认.这些DispatcherServlet.properties
在与DispatcherServlet
类相同的包中定义.它们是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
配置.
A DispatcherServlet
只是一个典型的Java EE Servlet
.你与你的典型进行注册
和
申报,或直接通过ServletContext#addServlet
在一个WebApplicationInitializer
或与任何机构弹簧开机使用.因此,您必须依赖Servlet规范中指定的url映射逻辑,请参阅第12章.另请参见
如何使用web.xml中的Servlet URL映射?
考虑到这一点,一个常见的错误是DispatcherServlet
使用url映射注册/*
,从@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; }
您可能希望将请求转发到路径上的JSP资源/WEB-INF/jsps/example-view-name.jsp
.这不会发生.相反,假设上下文名称为Example
,DisaptcherServlet
将报告
未找到HTTP请求的URI与映射
[/Example/WEB-INF/jsps/example-view-name.jsp]
在DispatcherServlet
名为"调度"
因为DispatcherServlet
映射到/*
并/*
匹配所有内容(除了具有更高优先级的完全匹配),DispatcherServlet
将选择处理forward
来自JstlView
(由...返回InternalResourceViewResolver
).几乎在每种情况下,DispatcherServlet
都不会配置为处理这样的请求.
相反,在这种简单的情况下,您应该注册DispatcherServlet
to /
,将其标记为默认的servlet.默认servlet是请求的最后一个匹配项.这将允许您的典型servlet容器在尝试使用默认servlet之前选择映射到的内部Servlet实现*.jsp
来处理JSP资源(例如,Tomcat JspServlet
).
这就是你在你的例子中看到的.