Spring Java Config using Autowired caused NPE - java

I am having difficulty understanding why something in Spring Java Config using #Autowired does not work.
First, I am trying to move all my #Autowired annotations in the Java Config classes. This has the effect of making my "POJOs" back into real POJOs. I can then not only test them easily outside of a Spring context, but can also use mock objects easily and readily.
So I first tried this:
#Configuration
public class Module3ConfigClass {
#Autowired
private Module1Bean1 module1Bean1;
#Autowired
private Module2Bean1 module2Bean1;
#Bean
public Module3Bean1 module3Bean1() {
return new Module3Bean1(module1Bean1, module2Bean1);
}
}
However, when the Module3Bean1 constructor is invoked, both passed in Beans are null. If you didn't follow my made up naming convention above, both of those beans would be created by a separate Java Config configuration file. Also note that everything is wired up correctly - I know this because everything works perfectly when the #Autowired tags are on the corresponding private member fields inside of Module3Bean1.
FWIW, I tried adding an #DependsOn annotation to module3Bean1() method, but had the same results. I guess I just would really like to understand this behavior, is it correct (I suspect it is, but why)?
Finally, I found an acceptable workaround shown here:
#Configuration
public class Module3ConfigClass {
#Bean
#Autowired
public Module3Bean1 module3Bean1(Module1Bean1 module1Bean1, Module2Bean1 module2Bean1) {
return new Module3Bean1(module1Bean1, module2Bean1);
}
}
This seems fine to me, but if anyone would care to comment on it, that would be welcome as well.

I think you came across same problem I just had. In my case problem was invalid xml configuration. In my module B I had config like :
<beans>
<context:component-scan base-package="com.moduleB"/>
<import resource="classpath:applicationContext-moduleA.xml"/>
</beans>
In moduleA context I placed "context:annotation-config" annotation.
When I change import/context order to :
<beans>
<import resource="classpath:applicationContext-moduleA.xml"/>
<context:component-scan base-package="com.moduleB"/>
</beans>
Autowiring for configuration class properties started to work.

We had the same issue and came to the conclusion that the error arose because we had a circular dependency where a BeanPostProcessor was involved.
A PropertyPlaceholderConfigurer (a BeanPostProcessor) has been configured to set its propertiesArray property with the help of another bean:
<bean id="globalPropertyPlaceholderConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
lazy-init="false" depends-on="javaLoggingConfigurer">
<property name="locations">
<list>
<value>classpath:config/host/${env.instance}.properties</value>
<value>WEB-INF/config/host/${env.instance}.properties</value>
</list>
</property>
<property name="ignoreResourceNotFound" value="true" />
<property name="propertiesArray" value="#{springPropertyFinder.findProperties()}" />
</bean>
The used springPropertyFinder bean to set the propertiesArray is not a BeanPostProcessor but a "normal" bean that gathers all Properties instances with:
public Properties[] findProperties() {
Map<String, Properties> propertiesMap = applicationContext.getBeansOfType(Properties.class);
for (String title : propertiesMap.keySet()) {
PropertiesLoggerUtil.logPropertiesContent(logger, "Springcontext Properties ("+title+")", propertiesMap.get(title));
}
return propertiesMap.values().toArray(new Properties[propertiesMap.size()]);
}
The #Configuration class contained a bean of type Properties
So our assumption is that the #Configuration class has been created without being processed by the ConfigurationClassPostProcessor (also a BeanPostProcessor), because the PropertyPlaceholderConfigurer depends on the springPropertyFinder, which depends on the properties bean in the #Configuration class. The order of the BeanPostProcessors is probably not setup right under these circumstances.
This described setup worked in XML, but not with Java config.

Related

Spring Configuration xml file with two beans of the same type NoUniqueBeanDefinitionException

I am trying to set up a test applicationContext file for running integration tests on a project that I am working on.
I have two classes that have fields that are marked as #Resource. One class I can change and one I cannot as it is imported from a different project that I don't have any permissions to change. I cannot get my configuration file to set these #Resouces fields without giving me an org.springframework.beans.factory.NoUniqueBeanDefinitionException.
Simple example:
appconfig.xml file
...Typical spring setup...
<bean id="baseUrl" class="java.lang.String">
<constructor-arg value="myURL"/>
</bean>
<bean id="supportedLang" class = "java.lang.String">
<constructor-arg value="en"/>
</bean>
Class that uses baseURL, (I have control to change, simplified Version)
#Service("myService")
public class MyService implements AnotherService{
#Resource
private String baseUrl;
public String getBaseUrl(){return baseUrl;}
public void setBaseURL(String baseURL){this.baseUrl = baseUrl;}
}
Class that uses supportedLang (I don't have access to change this class simplified version)
#Service
public class LangSupportImpl implements InitializaingBean, LangSupport{
#Resource(name= "supportedLang")
private String twoLetterSupportedLang;
public getTwoLetterSupportedLang(){return this.twoLetterSupportedLang;}
}
If I don't set up the beans in the application config file I get a no bean defined error instead.
Any help would be greatly appreciated.
Try to use #Resource(name = "baseUrl") in your MyService class. This will tell Spring which exact bean to take and will resolve ambiguity.
Another option is to change XML configuration and add primary="true" to declaration of baseUrl bean

Any way to use Spring #Value annotations for non-singleton beans?

Currently I use the following method to inject properties into beans:
app.properties:
SingletonBean.valueA=this is a value
spring.xml:
<context:property-placeholder location="classpath:app.properties"/>
<context:component-scan base-package="..."/>
SingletonBean.java:
#Component
public class SingletonBean {
#Value("${SingletonBean.valueA}")
private String valueA;
}
This works great and is extremely convenient to be able to keep all my configs in a single, simple properties file. Is there any way I could extend this to work with multiple Beans of the same class? I need to do the following, with the 2 beans having different properties:
#Autowired private SampleBean beanA;
#Autowired private SampleBean beanB;
I know I can use the #Qualifier(name=...) annotation to support the following xml:
<bean id="beanA" class="SampleBean">
<property name="key1" value="A1"/>
<property name="key2" value="A2"/>
</bean>
<bean id="beanB" class="SampleBean">
<property name="key1" value="B1"/>
<property name="key2" value="B2"/>
</bean>
But with this I am forced to use old style setters in my SampleBean class, where I would prefer to use the newer #Value annotations.
Anyone know of a way to accomplish what I want that remains most consistent with how I am currently injecting my other beans?
The simple solution is just to inject all of the properties into the bean that utilizes the 2 SampleBean instances and create them with new instead. In my real code however there are actually 3 instances, each with 15 or so properties. This is much more cruft and repetition than I would like.

Initialize Spring Bean as late as possible

I have a follofing situation int "super-context.xml":
<bean id="conf" class="ee.Conf"/>
<bean id="service" class="ee.Serivce">
<property name="conf" ref="conf">
</bean>
Now I want to use this "super-context.xml" in various different projects. Say "sub-context.xml" has:
<import resource="super-context.xml"/>
<bean id="subConf1" class="ee.SubConf">
<property name="confloc" value="classpath:ee/customconf1.sss" />
</bean>
<bean id="subConf2" class="ee.SubConf">
<property name="confloc" value="classpath:ee/customconf2.sss" />
</bean>
...
<bean id="subConfn" class="ee.SubConf">
<property name="confloc" value="classpath:ee/customconfn.sss" />
</bean>
ee.Conf is something as follows:
public class Conf ... {
...
public void addSubConf(Resource res) {
//configuration resolving from res
}
...
}
ee.SubConf is something as follows:
public class SubConf ... {
...
#Autowired
ee.Conf superConf;
...
public void setConfloc(Resource res) {
superConf.addSubConf(res);
}
...
}
The problem aries on context load. Beans are initialized in following order (due to ordering in context file): conf, service, subConf1, subConf2, ... subConfn
But service bean actually depends on all the subConf beans (although this can't be deducted from the context definition itself). It loads OK when import in "sub-context.xml" is added after subConf bean definitions.
Reason behind this is implementing modularity. Is it possible to force a bean to load as late as possible ("service" bean in the example) or make beans of certain type load as soon as possible ("subConf" beans in the example), since fixed ordering of beans in "sub-context.xml" partly kills the wished modularity
Or is theree a more pure way to achieve this type of modularity?
I would say that you are approaching the problem in a wrong way. The SubConf shouldn't have a dependency on the Conf to start with. Simply inject the collection of SubConf objects in your Conf object.
public class Conf {
#Autowired
private List<SubConf> subconfs;
}
That way you eliminate the need for the SubConf to call the Conf class and this will remove your circular dependency.
See the Spring reference guide for more information on autowiring.
You can use depends-on
<bean id="beanOne" class="foo.Bar" depends-on="beanTwo" />

SpringMVC: Variables in Annotations

I have the following controller defined:
#Controller
#RequestMapping("/test")
public class MyController extends AbstractController
{
#Autowired
public MyController(#Qualifier("anotherController") AnotherController anotherController))
{
...
}
}
I'm wondering if it's possible to use variables in the #Qualifier annotation, so that I can inject different controllers for different .properties files, e.g.:
#Controller
#RequestMapping("/test")
public class MyController extends AbstractController
{
#Autowired
public MyController(#Qualifier("${awesomeController}") AnotherController anotherController))
{
...
}
}
Whenever I try I get:
org.springframework.beans.factory.NoSuchBeanDefinitionException:
No matching bean of type [com.example.MyController] found for dependency:
expected at least 1 bean which qualifies as autowire candidate for this
dependency. Dependency annotations:
{#org.springframework.beans.factory.annotation.Qualifier(value=${awesomeController})
I've included the following bean in my config.xml file:
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:config/application.properties</value>
</list>
</property>
</bean>
But the bean doesn't work unless I declare the bean explicitly in the xml file.
How do I do this with annotations??
First I think it is bad practice to make the dependency injection rely on configuration properties. You are probably going a wrong direction trying to do this.
However to answer your question: accessing placeHolder properties requires the dependency injection to be finished. To make sure it is, you can put your code that accesses the property inside a #PostContruct annotated method.
You will need to retrieve the bean manually from the applicationContext using getBean() method.
#Value("${awesomeController}")
private String myControllerName;
#PostConstruct
public void init(){
AnotherController myController = (AnotherController) appContext.getBean(myControllerName);
}
I'm not sure if what you're doing is possible but I can suggest a slightly different approach, but only if you're using Spring 3.1+. You could try using Spring Profiles.
Define the different controllers you want, one per profile:
<beans>
<!-- Common bean definitions etc... -->
<beans profile="a">
<bean id="anotherController" class="...AnotherController" />
</beans>
<beans profile="b">
<!-- Some other class/config here... -->
<bean id="anotherController" class="...AnotherController"/>
</beans>
</beans>
Your Controller would lose the #Qualifier and become something like:
#Autowired
public MyController(AnotherController anotherController) {
...
}
Then at runtime you can specify which controller bean you want to use by activating the corresponding profile using a system property, e.g.:
-Dspring.profiles.active="a"
or:
-Dspring.profiles.active="b"
It may be possible to set profiles based on a property file but you can find out more about Spring Profiles from this post on the Spring blog. I hope that helps somewhat.

Spring #Value annotations don't work, returning null

I have the following class:
#Component
public class MyClass {
#Value("${main.url}") private String mainUrl;
the following XML context:
<context:annotation-config/>
<context:component-scan base-package="mypackage"/>
<context:property-placeholder file-encoding="UTF-8" location="classpath:/app.properties" ignore-unresolvable="true"/>
and prop file:
main.url=veryniceurl.com
Injection doesn't work, it is always null.
I read a lot of similar examples and I thought that everything is ok but it isn't. Can anyone tell me if I forgot about something? I'm working with Mule ESB.
#Value doesn't seem to work with Mule. Instead you need to wire it up through the Mule XML, where I assume you are loading your component as a Spring Bean:
<spring:bean id="MyClass" class="com.example.MyClass">
<spring:property name="mainUrl" value="${main.url}"/>
</spring:bean>
Give an id to your properties and use this syntax :
#Value("#{jetProperties['jetBean.name']}")
<!-- define the properties file to use -->
<util:properties id="jetProperties" location="classpath:/jet.properties" />
From http://chrislovecnm.com/2010/03/08/spring-3-java-based-configuration-with-value/
Did you add the context placeholder in the dispatcher-servlet.xml ? As per here, Spring #Value annotation in #Controller class not evaluating to value inside properties file they seem to have solved it by adding it there instead of the application context

Categories