I used Spring Roo (with Spring 3.0) to create a simple project with a few domain objects and controllers. I wanted to experiment with using the @Component annotation and autowiring, but was having severe problems trying to autowire a String value (the url of a service) into my @Component managed bean, the string being contained in the xml of the application context. After searching several blogs, this is what I found:
- it is not possible to autowire a primitive value using annotations
- it WAS not possible before Spring 3, now you can do it with Spring Expression Language (spEL)
Here was my next to last attempt:
webmvc-config.xml:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!-- The controllers are autodetected POJOs labeled with the @Controller annotation.
<context:component-scan base-package="org.tcl" use-default-filters="false">
<context:include-filter expression="org.springframework.stereotype.Controller" type="annotation"/>
</context:component-scan> -->
<!-- The controllers are autodetected POJOs labeled with the @Controller annotation. -->
<context:component-scan base-package="org.tcl" />
<context:annotation-config/>
<!-- Turns on support for mapping requests to Spring MVC @Controller methods
Also registers default Formatters and Validators for use across all @Controllers -->
<mvc:annotation-driven/>
<util:properties id="appProperties" location="classpath:/app.properties"/>
...
TclApiService.java:
package org.tcl.service;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class TclApiService implements ApiService {
@Value("#{appProperties['tclApiUrl']}") String apiUrl;
@Override
public ApiResponse call() {
return null; //breakpoint here, just to check out the value of the apiUrl property
}
public void setApiUrl(String apiUrl) {
this.apiUrl = apiUrl;
}
}
app.properties:
tclApiUrl=http://www.mydomain.org/api/courses
No matter WHAT I tried, the tc server startup would fail with errors such as:
No matching bean of type [java.lang.String] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
or, in the case of my final spEL attempt:
Field or property 'appProperties' cannot be found on object of type 'org.springframework.beans.factory.config.BeanExpressionContext'
Turns out, this is what is happening:
Since the <context:component-scan> tag occurs before the “appProperties” bean definition in webmvc-config.xml, the bean definition is not available when the @Component-ized class is being constructed. The fix is simple, just move the “appProperties” bean definition above any <context:component-scan> tag. In Roo, keep in mind, at least in my case, that there were actually 2 <context:component-scan> tags, one in applicationContext.xml and one in webmvc-config.xml. Putting my properties bean definition above the one in applicationContext.xml solved the problem. Tc Server now started successfully and the property was populated in my @Component class.
Nowhere in official spring documentation could I find the stipulation that it actually matters where the <context:component-scan> tag is placed. Initially I thought that spring might maintain a map of unresolved dependencies which are satisfied in a round robin fashion, but nope, the xml dependencies are resolved first, then the annotated ones – at least in this simple case.