How to do string concatination in nested SpEL expression - spring

I am trying to do a stringConcat for a neste SpEL expression -
I am getting hostName from a bean
<bean id="localhost" class="java.net.InetAddress" factory-method="getLocalHost" />
Now in another bean I want to append hostName in the input to my constructor based on some regex match.
<bean id="worker" class="a.b">
<constructor-arg value="#{localhost.hostName matches '.*north-usa.*' ? '${taskList}-#{conf.version}-#{localhost.hostName}' : '${taskList}-#{conf.version}' "/>
</bean>
For some reason this is output of this is the literal string where only taskList is getting resolved -
taskListA-#{conf.version}-#{localhost.hostName}
I want to achieve
taskListA-someConfVersion-someHostName
So far I have tried
<bean id="worker" class="a.b">
<constructor-arg value="#{localhost.hostName matches '.*north-usa.*' ? "${taskList}-#{conf.version}-#{localhost.hostName}" : "${taskList}-#{conf.version}" "/>
</bean>
<bean id="worker" class="a.b">
<constructor-arg value="#{localhost.hostName matches '.*north-usa.*' ? '${taskList}-conf.version-localhost.hostName' : '${taskList}-#conf.version' "/>
</bean>
<bean id="worker" class="a.b">
<constructor-arg value="#{localhost.hostName matches '.*north-usa.*' ? '#(${taskList}-conf.version-localhost.hostName)' : '${taskList}-conf.version' "/>
</bean>
But none of them worked. Any help will be appreciated.

I'm not aware if there is something like nested expressions in SpEL, but I know how to do string concatenation:
#{localhost.hostName matches '.*north-usa.*' ? '${taskList}-' + conf.version + '-' + localhost.hostName : '${taskList}-' + conf.version}

Related

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

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}" />

How can I set default value for bean with String type?

I have a bean
<bean id="directoryName" class="java.lang.String"/>
I need to set default value for it. How can I do it?
Since String has a constructor:
<bean id="directoryName" class="java.lang.String">
<constructor-arg value="DEFAULT_VALUE"/>
</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>

char[] to String Conversion in Spring configuration

I have bean A that returns an attribute as char[] and another bean that expects an attribute as String and I would like to inject the attribute from bean A to bean B.
When I do this:
<bean id="B" class="....">
<property name="password" value="#{A.password}" />
</bean>
<bean id="A" class="...">
</bean>
The error I'm getting is:
Cannot convert value of type [char[]] to required type [java.lang.String] for property 'password': no matching editors or conversion strategy found
Any idea how to resolve this?
Perhaps by using an expression language syntax?
Perhaps by registering some sort of a converter like org.springframework.beans.propertyeditors.CharArrayPropertyEditor in the configuration before injecting the char[] attribute?
Looks like this will work:
<property name="password" value="#{new java.lang.String(A.password)}" />

define a string in spring context

I have three (A,B,C) spring context.xml, A is for the basic configuration, B and C import the A.
In a bean on A I have:
<bean class="com.example.Ex">
<property name="aString" value="${myString}" />
</bean>
now I want to define the property myString on B and C context, is possible to do it without create and loads two different properties file?
You could try an alternative way by declaring bean of type String, instead of dealing with Properties.
This way:
A
<bean class="com.example.Ex">
<property name="aString" ref="str" />
</bean>
And then you declare in your B and C contexts the "str" reference this way:
B
<bean id="str" class="java.lang.String">
<constructor-arg value="string_1"/>
</bean>
C
<bean id="str" class="java.lang.String">
<constructor-arg value="string_2"/>
</bean>
For completeness here another way of creating a string:
Instead of calling the String constructor which forces a new object to be created unnecessarily it may be a better idea to use the valueOf method which can here serve as a "do nothing" constructor:
<bean id="str" class="java.lang.String" factory-method="valueOf">
<constructor-arg value="string_1"/>
</bean>
However this is only academic as the overhead of parsing the additional XML attribute which will cause strings to be created as well may be greater than the performance gain of calling valueOf instead of the constructor.
This is also one of the way.
<bean id="str" class="com.example.Ex">
<constructor-arg type="java.lang.String" value="INDIA"/>

Resources