I have a servlet packaged in an ear file, and I cannot get it to resolve @Value annotated properties.
The app is in two parts: a servlet packaged in a war file, which is then included by different applications packaged in an ear file. The application supplies the implementation of a class (ResourceManager) defined by an interface in the servlet, and also the property file containing the value of the property for the @Value annotated field.
The war file contains:
web.xml:
<servlet>
<servlet-class>... extends MessageDispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:/spring-ws.xml
classpath:/spring-app.xml
</param-value>
</init-param>
</servlet>
spring-ws.xml:
<context:annotation-config/>
<context:property-placeholder location="classpath:/spring-ws.properties"/>
The values in spring-ws.properties are referenced directly from spring-ws.xml, and all work fine.
The servlet class contains:
public class MyServlet extends MessageDispatcherServlet
@Autowired private ResourceManager resourceManager; // original annotation - got this to work
@Value("${app.name:}") private String appName; // added later - cannot get this to work
The app.name is optional, hence the trailing ":".
The ear file adds (packaged in a jar file in the ear lib directory):
spring-app.xml:
<context:property-placeholder location="classpath:/spring-app.properties"/>
<bean class="ResourceManagerImpl">
...
</bean>
spring-app.properties:
app.name=myApp
My first problem was with the @Autowired annotation: not sure I ever got to the bottom of that correctly, but I managed to get it to work by adding this code to the servlet:
@Override
protected void initFrameworkServlet() throws ServletException {
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
bpp.setBeanFactory(getWebApplicationContext().getAutowireCapableBeanFactory());
bpp.processInjection(this);
}
This may not be the best way to fix this, but it was the first I found and it worked, so I used it and moved on. Then I added a @Value annotated field, and I can't find a way to make this work. The javadocs for AutowiredAnnotationBeanPostProcessor say that it should process @Value annotations as well, but it doesn't appear to: the appName property is always blank (presumably from the default).
I do have a workaround, using beans:
@Autowired @Qualifier("appName") private String appName;
<bean id="appName" class="java.lang.String">
<constructor-arg value="myApp"/>
</bean>
But I'm sure there must be a better way of doing this. I've read this post Difference between applicationContext.xml and spring-servlet.xml in Spring Framework, but I'm not sure it applies: I don't think I even have an application context here, just a servlet context defined in the web.xml for the (single) servlet class.
I've also seen this: Spring 3.0.5 doesn't evaluate @Value annotation from properties, which looks like the same problem, but the solution there appeared to be to move the "<context:property-placeholder>" from the application context to the servlet context, whereas as I said before, I don't think I even have an application context here...
Finally, I've had a quick go at simplifying the problem, removing the separate app and packaging everything in a single war, but I don't think that is the problem (although could obviously be wrong...): spring can resolve the class path for the app.xml file so it must be able to resolve the class path for the app.properties file.
Any help much appreciated, but I guess at this point it is a bit of an academic interest, as I have a workable solution (using beans) and the people that pay the bills don't really care how it works as long as it works! I'd just like to know so next time I don't have to copy (what I think is) a flawed workaround.
Using spring-ws-core 2.1.4, which pulls in spring 3.2.4 via maven dependencies.
Thanks
Update - Solved
Turns out the key to fixing this was M Deinum's comment that "You also have a duplicate element...". I had two separate PropertyPlaceholderConfigurers in the same context, so the second was ignored and its properties never used. There was no problem with the context itself (which is a servlet context not a root context), or the annotation processing.
So the fix was simply to remove the property placeholder from the spring-app.xml, and combine both property files in the spring-ws.xml
<context:property-placeholder
location="classpath:/spring-ws.properties,
classpath:/spring-app.properties"/>
TL;DR's answer here Spring: namespace vs contextConfigLocation init parameters in web.xml is also an awesome explanation of how the different contexts are processed!
Cheers,