3

I'm in the early stages of learning to use Spring MVC. I've created a controller and applied a RequestMapping annotation to it. When I run the project, the index page displays as expected at index.htm, but when I navigate to the URI that should be pointing to my controller, I get a 404 error, even though the controller seems to have been detected by Spring and started. Please help me understand what I am failing to grasp here:

Here is my web.xml:

<?xml version="1.0" encoding="UTF-8"?> <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/applicationContext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>2</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <session-config> <session-timeout> 30 </session-timeout> </session-config> <welcome-file-list> <welcome-file>redirect.jsp</welcome-file> </welcome-file-list> 

Here is my applicationContext.xml:

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd> <context:spring-configured/> <context:component-scan base-package="org.blah.blah"/> 

Here is my dispatcher-servlet.xml:

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd"> <mvc:annotation-driven/> <bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping"/> <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="mappings"> <props> <prop key="index.htm">indexController</prop> </props> </property> </bean> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver" p:prefix="/WEB-INF/jsp/" p:suffix=".jsp" /> <bean name="indexController" class="org.springframework.web.servlet.mvc.ParameterizableViewController" p:viewName="index" /> 

And, finally, here is my controller.

@Controller @RequestMapping(value = "/hello") public class Ctrl { @RequestMapping(method = RequestMethod.GET) @ResponseBody public String hello(){ return "hello!"; } } 

EDIT: This is what my browser displays: enter image description here

9
  • What is your context path and what is the URL you hit that caused the 404? Commented Feb 18, 2014 at 0:54
  • @SotiriosDelimanolis My context path is /blah, and I've tried a dozen different URLs - /blah/hello is the one I would expect to work... right? Commented Feb 18, 2014 at 0:56
  • I know you're trying to hide the real names, but please make everything consistent in your controller class and the configuration. Also, the 404 you get, what does the error message from Tomcat say? Commented Feb 18, 2014 at 0:57
  • @SotiriosDelimanolis - that's the interesting thing: There is no error message - it's just blank. The description says "The requested resource is not available". And I'm actually not trying to hide anything here - in an effort to figure out what's going on, I created an actual package called blah. What is above was c/p'd directly from my source files Commented Feb 18, 2014 at 1:00
  • What does your browser say is missing when it gives the 404? It should give a resource path. Commented Feb 18, 2014 at 1:04

2 Answers 2

3

I was half-wrong in the comments to gravityplanx' answer.

When you specify a <mvc:annotation-driven /> in your servlet configuration, Spring registers a RequestMappingHandlerMapping bean which is meant to collect and map all your @Controller handler methods, ie. those annotated with @RequestMapping.

It does this by looking at all the beans in the current context. Remember that when you load a context with DispatcherServlet, call it the servlet context, if a context was loaded by a ContextLoaderListener, call it the root context, the root context is made a parent of the servlet context. By default, the RequestMappingHandlerMapping bean does not look at the root context, which is a parent of the servlet context.

In your case, the @Controller beans are declared implicitly in the root context (your applicationContext.xml) because of the <context:component-scan/> which scans classes annotated with @Component or any of its specializations (@Controller for instance) and creates beans for them. So these beans are registered in the root context, where the RequestMappingHandlerMapping can't find them.

If instead you declare the <context:component-scan /> in the servlet context, then the @Controller (and other) beans are created in the servlet context where they are available to the RequestMappingHandlerMapping which can then register them to handle requests.

Note that <context:component-scan /> on its own doesn't do anything for the MVC stack. It needs further configuration, like <mvc:annotation-driven />. You should aim to specify packages that contain servlet specific beans in the servlet context and application wide beans in the root context.

Here's some literature:

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks so much for the explanation, that helps a lot. I accepted the other answer because it was what solved the problem, but upvoted yours!
2

Your dispatcher servlet doesn't seem to be referenced correctly.

Replace:

<servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>2</load-on-startup> </servlet> 

With:

<servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>[insert the path to your dispatcher-servlet here]</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> 

And then move:

<context:spring-configured/> <context:component-scan base-package="org.blah.blah"/> 

From applicationContext to dispatcher-servlet

3 Comments

No, by default DispatcherServlet will look for a file named <name of servlet>-servlet.xml. The load on startup of 2 will have the same effect as 1 in this case, though it is weird. The component-scan doesn't need to be in the servlet context necessarily. As long as it creates a bean which mvc:annotation-driven can see.
Typically I agree, but I always find it useful to be as explicit as possible with errors like this. Relying on the build to handle things automatically sometimes results in issues, but explicitly declaring your targets means that if there's a problem it's with something you typed.
After moving the two <context... /> lines to dispatcher-servlet, it worked! In the interest of helping me wrap my head around what's going on here, can you explain what exactly I did by moving those two lines, and why it worked?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.