I have a Spring web app with an applicationContext.xml and a dispatcher-servlet.xml configuration. I've defined the <context:component-scan />
in applicationContext.xml, but when I run my app the Controllers are not found unless I also add <context:component-scan />
to the dispatcher-servlet.xml. I'm using the same base-package in both, so that's not the issue.
I am confused, because I thought that the applicationContext.xml was a parent of dispatcher-servlet.xml. Wouldn't putting <context:component-scan />
in applicationContext.xml suffice?
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath: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>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/dispatcher-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
EDIT: I am also using mvc:annotation-driven in the dispatcher-servlet.xml, which is supposed to pick up Controllers (I thought?).
EDIT 2: Here are the config files. I removed a bunch of Spring Security and OAuth settings from applicationContext.xml (for security reasons and being they probably aren't relevant anyway).
applicationContext.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:sec="http://www.springframework.org/schema/security" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p" xmlns:c="http://www.springframework.org/schema/c"
xmlns:context="http://www.springframework.org/schema/context" xmlns:oauth="http://www.springframework.org/schema/security/oauth2"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd
http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<context:component-scan base-package="bar.foo"/>
<context:property-placeholder location="classpath:my.properties" />
<bean class="bar.foo.ServicesConfig" />
</beans>
dispatcher-servlet.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p" xmlns:c="http://www.springframework.org/schema/c"
xmlns:context="http://www.springframework.org/schema/context" 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/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<context:component-scan base-package="bar.foo.controller" />
<mvc:annotation-driven/>
<mvc:default-servlet-handler />
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
<property name="order" value="2" />
</bean>
<bean id="contentViewResolver" class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="mediaTypes">
<map>
<entry key="json" value="application/json" />
</map>
</property>
<property name="defaultViews">
<bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />
</property>
<property name="order" value="1" />
</bean>
</beans>
EDIT 3: Okay, this is interesting. My services and dao classes are in a different project (JAR) that I reference from the web project. I am using java-based config and referencing it from the applicationContext.xml:
<bean class="bar.foo.config.ServicesConfig" />
So, this means there are only Controller annotations in my web project (where applicationContext.xml is located). In retrospect, removing context:component-scan from my applicationContext.xml should not have any affect, since there are no annotations except for @Controller ones (FIX to EDIT: there are some @Autowired annotations). But, when I remove the context:component-scan from applicationContext.xml, it says that the Controllers (found from dispatcher servlet scan) cannot find my Service classes. Shouldn't the reference to the ServicesConfig be enough? Here is the ServicesConfig class for references - it has its own component scan for the Services, which are a different package from what the applicationContext.xml was scanning.
@Configuration
@ComponentScan({ "some.other.package", "another.package" })
@ImportResource({ "classpath:commonBeans.xml" })
@PropertySource({ "classpath:services.properties",
"classpath:misc.properties" })
public class ServicesConfig {
// Bean definitions //
}
SOLUTION:
When I removed context:component-scan from my root context, the Controllers were not picking up the autowired services beans. This was because the root context references my services java-based config Bean, but I did not have the root context setup to scan for Components. Hence, when I add component scanning to the root context (applicationContext.xml) everything works. Here is what I have now:
applicationContext.xml:
<bean class="bar.foo.config.ServicesConfig" />
<context:component-scan base-package="bar.foo.config" />
dispatcher-servlet.xml:
<context:component-scan base-package="bar.foo.controller" />
I have the web context setup to pickup Controller, Autowired, and any other annotations in the controller package - I'm not sure if this is best practice or not.