How to resolve Spring #Value annotations in a web servlet? - java

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,

Your servlet isn't a spring managed bean it is managed by the Servlet container. Hence the reason why #Autowired doesn't work in the first place. Spring will only process beans it knows.
Both the ContextLoaderListener and your servlet (basically any servlet that extends FrameworkServlet) have there own instance of an ApplicationContext. The one from the ContextLoaderListener is used as a parent by the context constructed in the servlet. So you indeed have an ApplicationContext in your servlet.
Now for this #Value to resolve properly you need to have a PropertyPlaceHolderConfigurer to resolve the placeholders. As PropertyPlaceHolderConfigurer is a BeanFactoryPostProcessor it only operates on beans in the same context, so the one loaded/configured by the ContextLoaderListener doesn't do anything for beans related to the context from your servlet. (Hence you need to add <context:property-placeholder ... /> element to the servlet context).
You also have a duplicate element in general you want to avoid that kind of situation as it will lead to problems at same time, either one overriding the other or you will get exceptions that placeholders cannot be resolved by either one of the PropertyPlaceHolderConfigurer
Your init code is also overly complex and you don't need the AutowiredAnnotationBeanPostProcessor simply use the available methods.
getWebApplicationContext().getAutowireCapableBeanFactory().autowireBean(this);
Which also does more then what only the AutowiredAnnotationBeanPostProcessor does.

for #Autowired annotation, you just need to have a class with those annotations:
#Configuration
#PropertySource("classpath:myFile.properties")
#ComponentScan("com.company.package")
The #Configuration is used by Spring to tell it's a class for configuration (it replaces the xml files that you talked about, xml conf file is Spring 2, annotation is new with Spring 3 and 4)
The #ComponentScan manage the IOC (#Autowire)
The #PropertySource load the prop file
Then the class to initialized at startup with the prop file:
#Component("myFileProperties")
public class MyFileProperties{
#Value("${app.name}")
private String appName;
public String getAppName() {
return appName;
}
public void setAppName(String appName) {
this.appName= appName;
}
}
Take a look here if you want it can inspire you (a conf file in a web context that I developped): https://github.com/ebrigand/RSSLiker/blob/master/src/main/java/com/mrm/rss/init/WebAppConfig.java

There is a workaround for this.
You can create a static field in a java class to store the value in.
public class SystemPropertyHelper {
public static String propertyValue;
}
And create a java class annotated with #Service or #Component, which you can inject the property in, and pass this value to the static field.
#Component
public class SpringBeanClass {
#Value("${propertyName}")
private String propertyValue;
#PostConstruct
public void init() {
SystemPropertyHelper.propertyValue = propertyValue;
}
}
This way you can always access the property value through the static field SystemPropertyHelper.propertyValue

Related

How to load beans according to Spring java conf from jar dynamically?

I have such app conf in, jar which will be added to the classpath after startup:
#Configuration
#ComponentScan("com.transportexchangegroup.testConf")
public class AppConf {
}
How to load beans dynamically? I saw solutionsm when it is required to write and add bean definitions, but if we do not know everything about new beans and just want to load them automatically?
Class conf = jarService.loadClass("com.x.testConf.AppConf");
((AnnotationConfigServletWebServerApplicationContext) applicationContext).register(conf);
((AnnotationConfigServletWebServerApplicationContext) applicationContext).refresh();
As I see, refresh() turns off web app started locally from IDE.
Do you know any other solutions or what is wrong? Will this work for spring rest controllers from jar?
I'm not sure what do you mean "dynamically".
In general you can load beans if some condition applies, usually depending on the configuration. So you can do something like this:
application.properties: // or yaml it doesn't matter
feature.enabled=true
#Component
#ConditionalOnProperty(name="feature.enabled", havingValue="true", matchIfMissing="true" / "false") // matchIfMissing depends on whether you want the bean to be loaded if the property is not defined
public MyBean {
}
Some caveats:
If you have many beans that depend on "business" feature in order to avoid placing #ConditionalOnProperty you can do one of the following:
Define your own #Component annotation:
// runtime retention, place on class
#Component
#ConditionalOnProperty(...)
#MyFeatureComponent
... and use it in all the beans that define the feature:
#MyFeatureComponent
public class MyBean
{}
Use Java Configuration instead of annotations:
#Configuration
#ConditionalOnProperty(...)
public class MyFeatureConfiguration {
#Bean
public MyBean myBean(){return new MyBean();}
#Bean
public MyAnotherBean myAnotherBean(){return new MyAnotherBean();}
}
In this case you don't need to place any annotation on MyBean at all.
Spring also has a concept of profiles which is just the same, something that it under the hood implemented with these conditionals.
It allows however to define configuration files per profile, so you might want to read about #Profile annotation as well.
As for the bean definitions - this is way more advanced stuff, in general when spring loads it "recognizes" which bean should be loaded and in which order and for doing that it creates a bean definition before loading the bean. So if you hook into this process you can define your own bean definitions if you want and spring will create beans based on these definitions as well. So basically its a hook that allows altering the bean defitions / create new one during the startup process and hence affect the actual beans that will be loaded into the application context.
I doubt, but if you really need that, read about Bean Factory Post Processors in spring.

Autowiring a DAO into a Service

I'm using Spring and am trying to autowire (using annotations) a DAO into a Service, which is then wired into a controller. Having
#Autowired
Movie movieDao;
on its own doesn't work, as I think the new method gets called, so that DAO isn't managed by Spring. The following does work, but it will look messy if I have to copy and paste that context configuration into each method
#Autowired
MovieDao movieDao;
#Override
public List<Movie> findAll() {
GenericXmlApplicationContext context = new GenericXmlApplicationContext();
context.load("classpath:app-context.xml");
context.refresh();
MovieDao movieDao = (MovieDao) context.getBean("movieDao", MovieDao.class);
return movieDao.findAll();
}
where this code is in my Service class. Is there a more elegant way to ensure that my DAO is initialised properly, rather than copying and pasting the first 4 lines of that method into each Service method?
[edit] The class that contains the code above is a class called MovieServiceImpl, and it essentially corresponds to the DataServicesImpl class in the architecture described on this page. (I'll add a summary/description of that architecture and what I'm trying to do soon). This is the code: http://pastebin.com/EiTC3bkj
I think that the main problem is that you want to instantiate your service directly (with new) and not with Spring:
MovieService movieService = new MovieServiceImpl();
When you do this, your MovieServiceImpl instance is constructed but not initialised (the field #Autowired MovieDao is null).
If you want to instantiate properly your object with field injection, you need to use Spring. As explained in the documentation or in this example, you can automatically detect all your annotated beans and initialize them in your context with the component scanning.
Example
In your case, using annotiations on (#Component, #Service, etc) and in (#Autowired, #Inject, etc) your beans, your project could look like this:
Spring configuration app-context.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- Use component scanning to auto-discover your beans (by annotation) and initialize them -->
<context:component-scan base-package="com.se325.a01" />
<!-- No need to declare manually your beans, because beans are auto-discovered thanks to <context:component-scan/> -->
</beans>
Entry point of your application App.java
package com.se325.a01;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.se325.a01.model.Movie;
import com.se325.a01.service.MovieService;
public class App {
public static void main(String[] args) {
// Let's create the Spring context based on your app-context.xml
ApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"app-context.xml"});
// Now your context is ready. All beans are initialised.
// You can retrieve and use your MovieService
MovieService movieService = context.getBean("movieService");
Movie matrixMovie = new Movie("Matrix");
movieService.create(matrixMovie);
}
}
In fact, when you are using Spring, it is really important to understand how the context is initialized. In the example above, it can be sum up as:
Your entry point App#main is called.
The configuration app-context.xml is loaded by ClassPathXmlApplicationContext.
The package com.se325.a01 is scanned thanks to the line <context:component-scan base-package="com.se325.a01" />. All annotated beans (#Component, #Service, etc) are contructed but not yet initialised.
When all the beans are constructed, Spring initialises them by injecting dependencies. In the example, the #Autowired annotations which mark the dependencies are also discovered thanks to the line <context:component-scan ... \>.
The context is ready with all beans :)
Notes
All this answer explains how you can use component scanning and annotations to use Spring in a main entry point. However, if you are developing a server application, the entry point is the WEB-INF/web.xml.
As #chrylis said, field injection is error prone. Prefer using constructor-based injection.

Liferay Configuration Action Class - Spring dependency Injection

In Liferay, the Configuration Action class is defined in liferay-portlet.xml
The problem is, if I use any spring dependency injection, it's not working.
<portlet>
<portlet-name>search</portlet-name>
<icon>/icon.png</icon>
<configuration-action-class>com.mypack.MyConfigurationAction</configuration-action-class>
<header-portlet-css>/css/main.css</header-portlet-css>
<footer-portlet-javascript>/js/main.js</footer-portlet-javascript>
<css-class-wrapper>search-portlet</css-class-wrapper>
<add-default-resource>true</add-default-resource>
</portlet>
Action Class implementation
public class MyConfigurationAction extends DefaultConfigurationAction {
private #Value("${test.property1}") String property1;
private #Value("${test.property2}") String property2;
}
How do I inject these properties into this Action class, without using ClassPathXmlApplicationContext and hard coding spring.xml file in the class
There are two ways to save preferences in portlet development[in liferay],
Through liferay specific way, which uses liferay-portlet.xml entry . cant be managed with spring.
JSR-286[portal agnostic], portlet EDIT mode.
While developing portlet with Spring MVC framework, its advisable to use portlet EDIT mode.
In Spring MVC portlet framework, you can map portlet requests by portlet mode.
For Example: Create controller class as below which will map to EDIT mode requests.
#Controller
#RequestMapping("EDIT")
public class PreferencesController
with two methods, one method with annotation #RenderMapping, responsible for view and other method with annotation #ActionMapping/#RequestMapping responsible for storing preferences.
Hope this would help.
Try this
portlet.xml
<supports>
.....
<portlet-mode>edit</portlet-mode>
</supports>
Controller class
#Controller
#RequestMapping(value = "EDIT")
public class XYZ{
}
HTH
First of all, "Configuration" is NOT "Edit" mode. If you enable Edit mode (as suggested by others), you'll get "Preferences" button in your portlet menu. It is a Liferay feature that you can override as per your requirement.
I have Not tried this myself but you can try to use #Autowired to AutoWire your MyConfigurationAction class (and possibly use #Required annotation if needed?). Don't forget to put <context:annotation-config/> in your applicationContext.xml file, if not already done.

Spring circular reference example

I have a circular reference in one of my projects at work using spring, which I am unable to fix, and fails with the following error at startup:
'org.springframework.security.authenticationManager': Requested bean is currently in creation: Is there an unresolvable circular reference?
I tried to recreate the same problem at a smaller level in a sample project (without all the details of my work project). I have however been unable to come up with a plausible scenario where spring fails with an error.
Here's what I have:
public class ClassA {
#Autowired
ClassB classB;
}
public class ClassB {
#Autowired
ClassC classC;
}
#Component
public class ClassC {
#Autowired
ClassA classA;
}
#Configuration
public class Config {
#Bean
public ClassA classA() {
return new ClassA();
}
#Bean
public ClassB classB() {
return new ClassB();
}
}
I have a similar scenario in my project, which fails, and I was expecting spring to complain in my sample project as well. But it works fine! Can someone give me a simple example of how to break spring with the circular reference error?
Edit: I fixed the issue using javax.inject.Provider. The only other difference in the 2 projects was the annotations used were javax.inject.Inject and javax.annotation.ManagedBean in place of #Autowired and #Component.
You could use #Lazy to indicate that the bean is lazily created, breaking the eager cycle of autowiring.
The idea is that some bean on the cycle could be instantiated as a proxy, and just at the moment it is really needed it will be initialized. This means, all beans are initialized except the one that is a proxy. Using it for the first time will trigger the configuration and as the other beans are already configured it will not be a problem.
From one Issue in Spring-Jira:
#Lazy annotation that can be used in conjunction with #Configuration
to indicate that all beans within that configuration class should be
lazily initialized. Of course, #Lazy may also be used in conjunction
with individual #Bean methods to indicate lazy initialization on a
one-by-one basis.
https://jira.springsource.org/browse/SJC-263
Meaning that annotating your bean as #Lazy would be enough. Or if you prefer just annotate the configuration class as #Lazy as follows:
#Configuration
#Lazy
public class Config {
#Bean
public ClassA classA() {
return new ClassA();
}
#Bean
public ClassB classB() {
return new ClassB();
}
}
If you implement an interface of your beans this will work quite well.
This is an old thread, so I guess you almost forgot about the issue, but I want to let you know about the mystery. I encountered the same problem, and mine didn't go away magically, so I had to resolve the problem. I'll solve your questions step by step.
1. Why you couldn't reproduce the circular reference exception?
Because Spring takes care of it. It creates beans and injects them as required.
2. Then why does your project produce the exception?
As #sperumal said, Spring may produce circular exception if you use constructor injection
According to the log, you use Spring Security in your project
In the Spring Security config, they do use constructor injection
Your beans which injects the authenticationManager had the circular reference
3. Then why has the exception gone away mystically?
The exception may or may not occur depends on the creation order of beans. I guess you made several *context.xml files or so, and load them with config something like below in web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:*-context.xml</param-value>
</context-param>
The xml files will be loaded by XmlWebApplicationContext class and the loading order of files are not guaranteed. It just loads files from the file system. The problem is here. There's no problem if the class loads the application context file first, because your beans are already created when they are used for the construction injection of Spring Security. But, if it loads the Spring Security context file first, the circular reference problem occurs, because Spring tries to use your beans in the constructor injection before they had been created.
4. How to solve the problem?
Force the loading order of the xml files. In my case, I loaded the security context xml file at the end of the application context file by using <import resource="">. The loading order can be changed depends on environments even with the same code, so I recommend setting the order to remove potential problems.
According to Spring documentation, it is possible to get Circular dependency issue or BeanCurrentlyInCreationException by using constructor injection.
The solution to fix the issue is to use setters instead of Constructor injection.
Reference http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/beans.html.

Why is Spring not wiring my #Autowired members in a dependent jar?

I'm building a Google App Engine app using Spring 3.1 and am having a problem getting members in one of my jars wired.
I have three projects:
server
server.model
server.persistence
I have an ant build script so that when my workspace builds, it creates jars for server.model and server.persistence, and puts them in the correct lib directory for the server project.
In server, I can autowire things from both server.model and server.persistence, but in server.model my server.persistence beans aren't getting wired even though they're the exact same as in server.
snippet from my servlet application config:
<context:component-scan base-package="com.impersonal.server"/>
<bean autowire="byType" id="appEngineDataStore" class="com.impersonal.server.persistance.AppEngineDataStore"/>
<bean autowire="byType" id="userList" class="com.impersonal.server.model.UserList"/>
I have the following code in both the server project and the server.model project, and only the server one gets fulfilled. Here's the one failing:
package com.impersonal.server.model;
import java.util.ArrayList;
import java.util.UUID;
import org.springframework.beans.factory.annotation.Autowired;
import com.impersonal.server.persistance.AppEngineDataStore;
import com.impersonal.server.persistance.IDataStore;
public class UserList extends ArrayList<User>
{
private UserList(){}
//this is always null, but the same line in a class in the other project works
private #Autowired AppEngineDataStore _dataStore;
public UserList(UUID userId, String tempId)
{
String poo = "poo";
poo.concat("foo ");
int i = 3;
}
}
Edit:
Just did a test in the server.model project trying to #Autowired something that I don't have defined as a bean in my application config, and didn't get any errors. I should have got a 'no such bean found' error like I do if I do the same thing for the server project.
Any ideas why?
I was instantiating my objects incorrectly. For framework objects and such like MVC controllers, you don't need to do anything to get your #Autowired members wired.
For objects I was creating on the fly, I wasn't going through the IOC container, that's why their dependencies weren't being fulfilled.
<context:component-scan/> tag searches for annotated classes.
If you are going to autowire class using #Autowire annotation, Autowiring class should be annotated with one of stereotype annotation (#Component,#Controller,#Service,#Repository). Spring resolves first annotation configuration and then xml configuration. This is written in spring doc as
Annotation injection is performed before XML injection, thus the latter configuration will override the former for properties wired through both approaches.
Check proof on spring doc.
So what you need to do is add annotations for classes from server project as well as server.model. Same in case of your third project server.persistence. Add annotations according to layers or functionality.
Try:
#Autowired(required = true)
private AppEngineDataStore _dataStore;
Instead of:
private #Autowired AppEngineDataStore _dataStore;
EDIT 1:
While using the autowire above, in your spring xml, try:
<bean id="appEngineDataStore" class="com.impersonal.server.persistance.AppEngineDataStore" scope="prototype"></bean>
Instead of:
<bean autowire="byType" id="appEngineDataStore" class="com.impersonal.server.persistance.AppEngineDataStore"/>
autowire in xml means slightly different thing. Instead of defining your bean in xml, you can annotate it as #Service, it will be discovered by the component-scan and the #Autowired will work.
In your xml configuration, use autowire-candidate property
<bean autowire="byType" id="appEngineDataStore" class="com.impersonal.server.persistance.AppEngineDataStore" autowire-candidate="true" />

Categories