How to set bean property value from other bean property value - java

For example we have bean beanA with string property propertyA:
<bean name="beanA" class="...">
<property name="propertyA"><value>some string value </value></property>
</bean>
And second bean beanB has also string property propertyB, and I want initialize this property with same value as beanA.propertyA, I thought I need to do something like this:
<bean name="beanB" class="...">
<property name="propertyB"><value>beanA.propertyA</value></property>
</bean>
But this is not works, expression beanA.propertyA threated as string value.
Probably this is go against IoC theory and not supported by Spring.
Thanks.

The util namespace has some pretty useful things.
You can use <util:property-path id="name" path="testBean.age"/>

In Spring 3 you can also use Spring Expression language:
<bean name="beanB" class="...">
<property name="propertyB"><value>#{ beanA.propertyA }</value></property>
</bean>

Related

Custom formatters not registered - ConversionNotSupportedException : Spring

I have a custom formatter #Component class DebitCardNumberFormatter implements Formatter<DebitCardNumber>{...} I have added this formatter to the FormattingConversionServiceFactoryBean by
<bean id="formattingConversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="formatters">
<set>
<ref bean="debitCardNumberFormatter"/>
</set>
</property>
</bean>
Now when I explicitly run the following code, it works correctly and the string becomes DebitCardNumber,
DebitCardNumber debitCardNumber=formattingConversionService.convert("1234-3242
-4533-3432",DebitCardNumber.class);
But if I try to inject a DebitCardNumber in another bean with a string value,
<bean id="cardDetails" class="com.amudhan.springcore.formatter.CardDetails">
<property name="debitCardNumber" value="1234-3242-4533-3432"></property>
</bean>
The container throws ConversionNotSupportedException.
org.springframework.beans.ConversionNotSupportedException: Failed to convert property value of type [java.lang.String] to required type [com.amudhan.springcore.formatter.DebitCardNumber] for property 'debitCardNumber'; nested exception is java.lang.IllegalStateException: Cannot convert value of type [java.lang.String] to required type [com.amudhan.springcore.formatter.DebitCardNumber] for property 'debitCardNumber': no matching editors or conversion strategy found`enter code here`
I am reading the Spring documentation and practicing the APIs. What
am I missing here ?
Previously when I created 'Converter's, I used 'converters' property
of ConversionServiceFactoryBean to add that converter and use it
automatically.
In the same way I have tried adding the formatter by using the
'formatters' property of FormattingConversionServiceFactoryBean. Is
there anything that I fundamentally missing ? Or Is my understanding
of Formatters is plain wrong ?
If It throws exception when I use spring to debitCardNumber it in the applicationContext, how come it is working correctly when I call the convert method explicitly from the app ? Even the beans are correctly created when I checked.
This is one way to register your custom formatter
<mvc:annotation-driven conversion-service="conversionService"/>
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="formatters">
<set>
<ref bean="debitCardNumberFormatter"/>
</set>
</property>
</bean>
Update
Alright after looking little deeper into the code. So spring is looking for conversion service bean in your application context with id "conversionService" in Abstract bean factory so it can use that to parse your bean instead of property editors(before Spring 3.0).
So now why it worked with converters but not formatters because your id name for custom formatter didn't match with the id("conversionService") spring is looking for.
Any other name for id it will not work and so it's not a problem between choosing converter or formatter.
Hope this helps.
This example with formatter should work.
<bean id="conversionService"
class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="formatters">
<set>
<ref bean="debitCardNumberFormatter"/>
</set>
</property>
</bean>

How to create java.util.Optional<T> object from Spring bean?

Java
public class MyObject{}
public class MyFactory{
private Optional<MyObject> myproperty;
public Optional<MyObject> getMyproperty{...}
public void setMyproperty{...}
}
Spring config xml (doesn't work)
<bean id="myproperty" class="java.util.Optional">
<constructor-arg>
<value>com.MyObject</value>
</constructor-arg>
</bean>
<bean id="myfactory" class="com.Myfactory">
<property name="myproperty" ref="myproperty" />
</bean>
Does spring support generics beans?
The reason for using Optional is it provide some useful features such as checking value if null. You can complete checking and further action in one line of code.
getMyproperty().ifPresent(id -> call.setId(id));
Seems the problem have nothing to do with generics.
You simply need to properly tell Spring to create the bean using a factory method, as Optional can only be created though factory methods. Something like:
<bean id="myproperty" class="java.util.Optional" factory-method="of">
<constructor-arg type="java.lang.Object" value="com.MyObject" />
</bean>
for which it is supposed to mean creating the myproperty bean by Optional.of(com.MyObject.class) (Change the factory-method to ofNullable if that's the one you want to use)
Another option is to use SpEL (Spring Expression Language):
<bean id="mybean" ...>
<property name="optProp" value="#{ T(java.util.Optional).of( #wrapme) }"/>
</bean>
Where "wrapme" is the name of a bean defined elsewhere that you want to wrap in java.util.Optional.

Determine for which aggregation a bean is created

I have the following definition:
<bean id="logger" factory-method="createLog" scope="prototype" class="com.test.beans.LogBean" ></bean>
<bean id="aone" class="com.test.beans.AggregationOne">
<property name="log" ref="logger"></property>
</bean>
<bean id="atwo" class="com.test.beans.AggregationTwo">
<property name="log" ref="logger"></property>
</bean>
Is it possible to recognize for which object (aone or atwo) bean 'logger' is being created?
Why I'm asking: in a legacy application I have one log instance for all classes. I want to change level for some packages, but can't do that (except using filters, what I don't want). For that purpose I want to utilize some spring magic, if it exists for that case )
I don't think it can be done this way. What you could try is a BeanPostProcessor implementation which detects common logger object in beans and replaces it with a specific one.

How do I load a bean value from a file with job parameter substitution?

In my spring batch project I can do something like this:
<bean id="exampleTasklet" class="my.custom.Tasklet">
<property name="message" value="job parameter value: #{jobParameters['arg1']}"/>
</bean>
and the message property will have a value taken from the spring batch job parameters. However, the value that I actually want to assign is very large and I don't want to put it in the xml file. I know this syntax doesn't work, but I would like to do something like:
<bean id="exampleTasklet" class="my.custom.Tasklet">
<property name="message" read-value-from-file="/path/to/file.txt"/>
</bean>
and that file would contain the line "job parameter value: #{jobParameters['arg1']}" which spring will parse as if the file content was in a value="" attribute.
Is there a nice way to do this?
I think what you are looking for is a PropertyPlaceholderConfigurer.
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="/path/to/file.properties" />
<property name="placeholderPrefix" value="#{" />
<property name="placeholderSuffix" value="}" />
</bean>
This is run by Spring as a bean processor and will attempt to resolve placeholder tokens. There is a default instance that will resolve against system properties, using this notation: ${propertyname}. For your notation, you would need to specify the placeholderPrefix/Suffix. When there are multiple bean processors, the order is determined by the order property. By default, if a processor fails to resolve a placeholder, execution fails, but this can be altered by setting ignoreUnresolvablePlaceholders. Since the mechanism is property driven, you probably want to consider a notation like:
<property name="message" value="job parameter value: #{jobParameters.arg1}"/>
Or, if what you're trying to convey is that arg1 is also a parameter, you might try:
<property name="message" value="job parameter value: #{jobParameters.${arg1}}"/>
Spring loops over the bean processors until no replacements are performed, or an exception is raised. So defining a property as ${something.${orOther}} is valid.
I would suggest you to use a String as file name and in your bean open that file.
I'm not sure if I get your problem right. I'm just suggesting something like Spring MessageBundle
Something like this:
<bean id="exampleTasklet" class="my.custom.Tasklet">
<property name="messagePath" location="/path/to/file.txt"/>
</bean>
And in your exampleTasklet read the file and do your thing (I'm not sure what it is)
If anybody came here to do something like this from a properties-file:
If you want a property from a .properties-file to appear in the JobParameters, you won't find ready-to-use solution. You can do the following:
Wrap a bean around your properties file.
Pass this bean to another one which has access to the JobParameters and can pump the properties from the file into that class.
Then you should be able to access your properties with Spring's Expression Language and do something like:
<bean id="myBean" class="my.custom.Bean">
<property name="prop" value="#{jobParameters['arg1']}"/>
</bean>
Alternatively, I think the solution proposed by Devon_C_Miller is much easier. You don't have the properties in your JobParameters then. But if the replacement in the XML configuration is the only thing you want, you only have to change your placeholders to:
${myPropFromFile}
Happy batching, everyone ;-)

Spring syntax for setting a Class object?

Is there a way to set a property in spring to, not an instance of a class, but the class object itself? i.e.
Rather than
<bean>
<property name="prototype" class="a.b.c.Foo">...
giving you an instance of "Foo", something like:
<bean>
<property name="prototype" class="java.lang.Class" value="a.b.c.Foo.class"...
edit:
best (working) solution so far - use the normal instantiation and derive the class in the setter. In terms of solutions I think this we'd describe this as "cheating":
<bean class="Bar">
<property name="prototype" class="a.b.c.Foo">...
public class Bar{
public void setPrototype(Object o){
this.prototypeClass=o.getClass();
edit:
dtsazza's method works as well.
edit:
pedromarce's method works as well.
<bean>
<property name="x">
<value type="java.lang.Class">a.b.c.Foo</value>
</property>
</bean>
That should work.
You could certainly use the static factory method Class.forName(), if there's no more elegant syntax (and I don't believe there is):
<property name="x">
<bean class="java.lang.Class" factory-method="forName">
<constructor-arg value="a.b.c.Foo"/>
</bean>
</property>
No. With a bean tag you instruct Spring on how to instantiate a class.
Would <property name="x" class="a.b.c.Foo.class"> work? That should be an instance of a Class object...

Categories