Spring - how to reference another property within the same bean? - spring

I have the following definition in my configuration:
<bean class="com.project.TimerBean">
<property name="delay" value="30000" />
<property name="interval" value="60000" />
<property name="invokeThis" value="com.project.TargetClass" />
<property name="receiver" value="XYZ" />
<property name="args" value="#{interval}" />
</bean>
I would like to set the value of args to the same value as interval (in this case, 60000) without having to hard-code the value. However, the above snippet doesn't seem to work. How should I change this?

# syntax (Spel Expressions) are supposed to work the way you wrote it. You need to replace
#{interval} to #{beanId.interval}.
For example, if the id of the bean you are creating is timerBean, #{timerBean.interval} is supposed to work. You cannot refer to a property directly even if it is a part of the bean definition.
It only works if the property you are referring to is a part of another bean.
<bean id="beanA" class="org.BeanA">
<property name="prop1" value="1000" />
</bean>
<bean id="beanB" class="org.BeanB">
<property name="prop2" value = "#{beanA.prop1}" />
<property name="prop3" value = "#{beanB.prop2}" />
</bean>
In the above example, prop2 gets initialised from prop1 correctly. But prop3 gets initialised to null.
If you look at AbstractAutowireCapableBeanFactory class and method,
protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs)
you can see that all the property values in a single bean definition are looped over and values are parsed. After all the values have been successfully parsed, only then they are set on the bean instance. In the above beanA and beanB example when prop3's value is getting parsed, spring looks for the value of prop2 on beanB which is not yet set and hence returns null.
AFAIK, there is no way around this except the way suggested by #Alex
PS: I am using spring version 4.1.6.RELEASE

Move interval value "60000" to the property file
yourVariableName = 60000
and change to:
<property name="interval" value="${yourVariableName}" />
<property name="args" value="${yourVariableName}" />

Related

spring property resolution from a spring expression

I want to resolve a property and specify the name of the property using a Spel expression. If I do this
<property name="host" value="#{T(...Constants).SINK_PROP_HOST}" />
the value gets resolved correctly to sink.host which is the value of this constant. Taking it a step further
<property name="host" value="${#{T(...Constants).SINK_PROP_HOST}}" />
This doesn't works. Any ideas how I can make it work. Essentially it should function the same as
<property name="host" value="${sink.host}" />
You can't do that, because properties are resolved before SpEL (you can do it the other way around).
This works...
public class Foo {
public static final String FOO = "foo.prop";
}
<util:properties id="props">
<prop key="foo.prop">bar</prop>
</util:properties>
<bean id="xx" class="foo.Bar">
<property name="foo" value="#{props[T(foo.Foo).FOO]}"/>
</bean>
You can, of course, load your "props" bean from a file.

How to get key value from properties file at runtime using spring

I want to get the changed key value from properties file at runtime.
test.properties file:
name = Hi
I have made Thread sleep with 5 sec and changed the key value as "Hello" but it is not getting changed.
<bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:test.properties</value>
</list>
</property>
<property name="ignoreResourceNotFound" value="true" />
<property name="ignoreUnresolvablePlaceholders" value="true" />
</bean>
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basenames">
<list>
<value>classpath:test</value>
</list>
</property>
<property name="cacheSeconds" value="1" />
</bean>
<bean id="tempBean" name="tempBean1" class="org.sri.spring.temp.Temp"
lazy-init="false" scope="prototype">
<constructor-arg type="String" value="${name}" />
</bean>
The ${name} placeholder inside the XML configuration is resolved using the PropertySourcesPlaceholderConfigurer which, as you may notice, has nothing in common with your reloadable messageSource.
It wouldn't work either way because Spring instantiates the tempBean only once: on application startup, by passing the value of ${name} to the constructor. The bean itself is not aware of where the value came from (and in particular, it doesn't care if the properties file gets edited).
If you really think it's a good idea to do it†, you can inject the entire messageSource into your tempBean, and get the current value in each call, e.g.:
public class Temp {
#Autowired // or wired in XML, constructor, etc.
private MessageSource messages;
public String sayHello() {
return messages.getMessage("name", null, Locale.getDefault());
}
}
† injecting a configuration-related object makes testing more difficult and is arguably bad design (mixing concerns). Have a look at the Spring Cloud Config project as it's likely that this is how the future is going to look like.
I do not think that Spring will update already existing beans when the properties change.
Try to create a new bean (prototype scope)

Bean is not properly generated from the application context retrieved from context aware

Below is my Pojo:
public class Triangle implements ApplicationContextAware, BeanNameAware{
private Point pointA;
private Point pointB;
private Point pointC;
My springs.xml's structure goes here:
<bean id="pointA" class="com.betta.springtest.Point">
<property name="x" value="0" />
<property name="y" value="0" />
</bean>
<bean id="pointB" class="com.betta.springtest.Point">
<property name="x" value="-20" />
<property name="y" value="0" />
</bean>
<bean id="pointC" class="com.betta.springtest.Point">
<property name="x" value="20" />
<property name="y" value="0" />
</bean>
<bean id="triangle-bean" class="com.betta.springtest.Triangle" autowire="autodetect"/>
<alias name="triangle-bean" alias="triangle" />
I have implemented ApplciationContextAware and setting context in the POJO. After the getting the bean from the context, I changed the value of one of the properties(bean) of the bean. Again I got the fresh child bean from the context, but I am not getting it with the values set in spring.xml. Here goes my code, can anyone tell me whats wrong here or is it the expected behavior?
public void draw(){
System.out.println("Point A "+this.pointA.getX()+" "+this.pointA.getY());
System.out.println("Point B "+this.pointB.getX()+" "+this.pointB.getY());
System.out.println("Point C "+this.pointC.getX()+" "+this.pointC.getY());
System.out.println("Changing the value of point B");
this.pointB.setX(0);
this.pointB.setY(0);
System.out.println(" After Changing the value of point B "+
this.pointB.getX()+" "+this.pointB.getY());
Point newPoint = (Point) this.context.getBean("pointB");
System.out.println("Restored value of point B "+
newPoint.getX()+" "+newPoint.getY());
}
In the above code, I want the object Point B with the values set in springs.xml, which I am not able to obtain. I am new to Spring, can anyone help me in understanding this concept?
Spring beans are singleton by default. That means that you will only have one instance of your bean per application context instance. And if you modify the instance retrieved from application context, it will still be modified when you retrieve it again, because only single instance of it exists.
You could use prototype bean scope, which means that new bean instance will be created each time you retrieve it from application context, in which case the code would behave as you expect.
<bean id="pointA" class="com.betta.springtest.Point" scope="prototype">
<property name="x" value="0" />
<property name="y" value="0" />
</bean>

Dependency Injection returned results of bean init-method

I have two simple beans. In the first bean it calls a init-method and return string value.
Now I want to this returned string from first bean init-method , inject to my second bean
helloWorldBean3 property newKey. Please advise me on how to implement this requirement.
<bean id="helloWorldBean2" init-method="loadKey"
class="com.java.snippets.enterprise.services.HelloWorld2">
<property name="key" value="${key.supportiveFile}" />
<bean id="helloWorldBean3"
class="com.java.snippets.enterprise.services.HelloWorld">
<property name="newKey" ref="???" />
</bean>
Try using Spring EL like so:
<bean id="helloWorldBean3"
class="com.java.snippets.enterprise.services.HelloWorld">
<property name="newKey" value=""#{helloWorldBean2.loadKey()}"" />
</bean>

constructor injection with value provided by another bean

I want to pass value to constructor(type string) with value provided by another bean.
Class BeanOne () {
BeanOne (String message) {
...
}
}
Below declaration will work
<bean id="beanOne"
class="com.abc.BeanOne">
<constructor-arg index="0" type="java.lang.String"
value="Hi There" /> // instead of value="Hi There", i want to use bean property (value="someBean.message")
</bean>
However I want another bean (say BeanTwo) to set value for message property for BeanOne. I tried nested property as given below but it does not work. Also message property is not visible directly in the class & is referred internally by another constructor & then by the method so i cannot use property injection& have to use only constructor injection
You can use the MethodInvokingFactoryBean to get your string value and then inject that into your bean.
<bean id="message" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject"><ref local="someBean"></property>
<property name="targetMethod"><value>getMessage</value></property>
</bean>
<bean id="beanOne" class="com.abc.BeanOne">
<constructor-arg index="0" type="java.lang.String" ref="message"/>
</bean>

Resources